-
Notifications
You must be signed in to change notification settings - Fork 41
tracking: add silicon tracker alignment workflow (MeasurementToMille) #2603
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,3 +2,5 @@ gaus | |
| mapp | ||
| te | ||
| ths | ||
| millepede | ||
| mille | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,157 @@ | ||||||||||||||||||||||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||||||||||||||||||||||||||
| // Copyright (C) 2025 ePIC Collaboration | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| #include <algorithms/service.h> | ||||||||||||||||||||||||||
| #include <cmath> | ||||||||||||||||||||||||||
| #include <cstdint> | ||||||||||||||||||||||||||
| #include <array> | ||||||||||||||||||||||||||
| #include <edm4eic/AlignmentDerivativeSetCollection.h> | ||||||||||||||||||||||||||
| #include <edm4eic/Measurement2D.h> | ||||||||||||||||||||||||||
| #include <edm4eic/MutableAlignmentDerivativeSet.h> | ||||||||||||||||||||||||||
| #include <edm4eic/TrackCollection.h> | ||||||||||||||||||||||||||
| #include <edm4hep/utils/vector_utils.h> | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| #include "MeasurementToMille.h" | ||||||||||||||||||||||||||
| #include "SiliconAlignmentLabels.h" | ||||||||||||||||||||||||||
| #include "algorithms/interfaces/ActsSvc.h" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| namespace eicrecon { | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| void MeasurementToMille::init() { | ||||||||||||||||||||||||||
| m_geo = algorithms::ActsSvc::instance().acts_geometry_provider(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const auto& trackingGeo = *m_geo->trackingGeometry(); | ||||||||||||||||||||||||||
| const auto& dd4hepDetector = *m_geo->dd4hepDetector(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Pass nullptr for the optional spdlog logger; the algorithms framework | ||||||||||||||||||||||||||
| // logger (info/debug methods below) is used instead. | ||||||||||||||||||||||||||
| m_surfaceToLayer = buildSiliconSurfaceLabelMap(trackingGeo, dd4hepDetector); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| info("Initialized: {} silicon surfaces mapped across {} known layers", m_surfaceToLayer.size(), | ||||||||||||||||||||||||||
| kNSiliconLayers); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| void MeasurementToMille::process(const Input& input, const Output& output) const { | ||||||||||||||||||||||||||
| const auto [tracks, measurements] = input; | ||||||||||||||||||||||||||
| auto [derivatives] = output; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| std::size_t nDerivatives = 0; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| for (const auto& track : *tracks) { | ||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||
| // Track quality cuts | ||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||
| const std::uint32_t ndf = track.getNdf(); | ||||||||||||||||||||||||||
| if (ndf == 0) { | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| const float chi2PerNDF = track.getChi2() / static_cast<float>(ndf); | ||||||||||||||||||||||||||
| if (chi2PerNDF > m_cfg.maxChi2PerNDF) { | ||||||||||||||||||||||||||
| debug("Skipping track: chi2/NDF={:.2f} > {:.2f}", chi2PerNDF, m_cfg.maxChi2PerNDF); | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const auto& mom = track.getMomentum(); | ||||||||||||||||||||||||||
| const float p = edm4hep::utils::magnitude(mom); | ||||||||||||||||||||||||||
| if (p < m_cfg.minMomentum) { | ||||||||||||||||||||||||||
| debug("Skipping track: p={:.3f} GeV/c < {:.3f} GeV/c", p, m_cfg.minMomentum); | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||
| // Iterate over measurements associated with this track | ||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||
| for (const auto& meas : track.getMeasurements()) { | ||||||||||||||||||||||||||
| const std::uint64_t geoId = meas.getSurface(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Look up which silicon layer this surface belongs to | ||||||||||||||||||||||||||
| auto layerIt = m_surfaceToLayer.find(geoId); | ||||||||||||||||||||||||||
| if (layerIt == m_surfaceToLayer.end()) { | ||||||||||||||||||||||||||
| continue; // not a silicon alignment surface | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| const int layerIndex = layerIt->second; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Skip fixed (reference) layers | ||||||||||||||||||||||||||
| bool isFixed = false; | ||||||||||||||||||||||||||
| for (const int fl : m_cfg.fixedLayers) { | ||||||||||||||||||||||||||
| if (fl == layerIndex) { | ||||||||||||||||||||||||||
| isFixed = true; | ||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if (isFixed) { | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||
| // Residual and uncertainty | ||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||
| // TODO: Replace with the proper residual from the ACTS Kalman smoother: | ||||||||||||||||||||||||||
| // residual = measured_local_u - predicted_local_u | ||||||||||||||||||||||||||
| // where predicted_local_u comes from Acts::detail::makeTrackAlignmentState(). | ||||||||||||||||||||||||||
| // The current approximation sets predicted = 0, so residual = measured_u. | ||||||||||||||||||||||||||
| // This is only valid for perfectly centred sensors and is a placeholder | ||||||||||||||||||||||||||
| // until the ACTS Alignment kernel is accessible from EICrecon. | ||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||
| const float measuredU = meas.getLoc().a; | ||||||||||||||||||||||||||
| const float residual = measuredU; // placeholder: predicted = 0 | ||||||||||||||||||||||||||
| const float residualUncert = std::sqrt(std::abs(meas.getCovariance().xx)); | ||||||||||||||||||||||||||
|
Comment on lines
+96
to
+98
|
||||||||||||||||||||||||||
| const float measuredU = meas.getLoc().a; | |
| const float residual = measuredU; // placeholder: predicted = 0 | |
| const float residualUncert = std::sqrt(std::abs(meas.getCovariance().xx)); | |
| const float measuredU = meas.getLoc().a; | |
| const float residual = measuredU; // placeholder: predicted = 0 | |
| const float variance = meas.getCovariance().xx; | |
| if (!std::isfinite(variance) || variance <= 0.f) { | |
| debug("Skipping measurement on surface {} in layer {}: non-physical local-u variance={}", geoId, | |
| layerIndex, variance); | |
| continue; | |
| } | |
| const float residualUncert = std::sqrt(variance); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||
| // Copyright (C) 2025 ePIC Collaboration | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <algorithms/algorithm.h> | ||
| #include <edm4eic/AlignmentDerivativeSetCollection.h> | ||
| #include <edm4eic/Measurement2DCollection.h> | ||
| #include <edm4eic/TrackCollection.h> | ||
| #include <cstdint> | ||
| #include <memory> | ||
| #include <string> | ||
| #include <string_view> | ||
| #include <unordered_map> | ||
|
|
||
| #include "ActsGeometryProvider.h" | ||
| #include "MeasurementToMilleConfig.h" | ||
| #include "algorithms/interfaces/WithPodConfig.h" | ||
|
|
||
| namespace eicrecon { | ||
|
|
||
| using MeasurementToMilleAlgorithm = algorithms::Algorithm< | ||
| algorithms::Input<edm4eic::TrackCollection, edm4eic::Measurement2DCollection>, | ||
| algorithms::Output<edm4eic::AlignmentDerivativeSetCollection>>; | ||
|
|
||
| /// Converts reconstructed tracks and measurements into Millepede-II alignment | ||
| /// derivative sets for silicon tracker alignment. | ||
| /// | ||
| /// For each track passing quality cuts the algorithm iterates over the | ||
| /// Measurement2D hits associated with the track, looks up the alignment layer | ||
| /// from the ACTS geometry, and fills one AlignmentDerivativeSet per | ||
| /// (track, surface) pair with approximate local and global derivatives. | ||
| /// | ||
| /// NOTE: The current implementation uses simplified (approximate) derivatives. | ||
| /// The correct derivatives require calling Acts::detail::makeTrackAlignmentState() | ||
| /// on the ACTS track states. Once the ACTS Alignment kernel is accessible from | ||
| /// EICrecon, replace the simplified code with the proper computation. | ||
| class MeasurementToMille : public MeasurementToMilleAlgorithm, | ||
| public WithPodConfig<MeasurementToMilleConfig> { | ||
| public: | ||
| MeasurementToMille(std::string_view name) | ||
| : MeasurementToMilleAlgorithm{ | ||
| name, | ||
| {"inputTracks", "inputMeasurements"}, | ||
| {"outputAlignmentDerivatives"}, | ||
| "Fills Millepede-II alignment derivatives for silicon tracker"} {} | ||
|
|
||
| void init() final; | ||
| void process(const Input&, const Output&) const final; | ||
|
|
||
| private: | ||
| /// Map from Acts::GeometryIdentifier (as uint64_t) to 0-based silicon layer index. | ||
| /// Built once during init() by walking the ACTS TrackingGeometry. | ||
| std::unordered_map<std::uint64_t, int> m_surfaceToLayer; | ||
|
|
||
| std::shared_ptr<const ActsGeometryProvider> m_geo; | ||
| }; | ||
|
|
||
| } // namespace eicrecon |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||
| // Copyright (C) 2025 ePIC Collaboration | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <vector> | ||
|
|
||
| namespace eicrecon { | ||
|
|
||
| /// Configuration for MeasurementToMille: selects tracks suitable for | ||
| /// silicon tracker alignment and controls which layers are constrained. | ||
| struct MeasurementToMilleConfig { | ||
| /// Maximum chi2/NDF for a track to be used in alignment | ||
| float maxChi2PerNDF = 5.0f; | ||
| /// Minimum track momentum [GeV/c] | ||
| float minMomentum = 1.0f; | ||
| /// 0-based layer indices to fix (skip) in Millepede, e.g. {0} for a reference layer | ||
| std::vector<int> fixedLayers; | ||
| }; | ||
|
|
||
| } // namespace eicrecon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The added note documents that MeasurementToMille requires edm4eic >= 8.10.0, but the build system doesn’t enforce this in CMake for this target. Consider turning this into an actual configure-time check (or raising EDM4EIC_VERSION_MIN) so users get a clear error instead of a compile failure when AlignmentDerivativeSet headers are missing.