Skip to content

tracking: add silicon tracker alignment workflow (MeasurementToMille) - #2603

Open
wdconinc wants to merge 1 commit into
eic:mainfrom
wdconinc:feat/silicon-alignment-workflow
Open

tracking: add silicon tracker alignment workflow (MeasurementToMille)#2603
wdconinc wants to merge 1 commit into
eic:mainfrom
wdconinc:feat/silicon-alignment-workflow

Conversation

@wdconinc

Copy link
Copy Markdown
Contributor

Summary

Adds the infrastructure for a Millepede-II global alignment workflow for the ePIC silicon tracking detectors.

Design

A two-stage, thread-safe pipeline:

  1. EICrecon (this PR) — fills edm4eic::AlignmentDerivativeSet collections per event via the new MeasurementToMille algorithm (JANA2 / PODIO, fully thread-safe).
  2. Offlinescripts/podio_to_mille.py in eic/epic converts the PODIO output to the binary Mille format consumed by pede.

Writing directly to a binary Mille file from inside JANA2 is not thread-safe; the PODIO intermediate step solves this cleanly.

New files

File Purpose
SiliconAlignmentLabels.h 12 silicon layers, label scheme layer×6+dof+1 (72 total); buildSiliconSurfaceLabelMap() walks ACTS geometry at init, maps GeometryIdentifier→layer via DD4hep path matching
MeasurementToMilleConfig.h maxChi2PerNDF, minMomentum, fixedLayers
MeasurementToMille.{h,cc} Per-event algorithm: quality-cuts tracks, iterates track.getMeasurements(), emits one derivative set per silicon surface hit
MeasurementToMille_factory.h JOmniFactory with PodioInput/PodioOutput and exposed ParameterRef for all config fields
src/global/alignment/ New alignment JANA plugin; inputs: CentralCKFTracks + CentralTrackerMeasurements; output: SiliconAlignmentDerivatives

Known limitations / TODOs

  • Local and global derivatives are currently simplified approximations ([1,0,0,0,0] local, [-1,0,0,0,0,0] global). The correct derivatives require calling Acts::detail::makeTrackAlignmentState() on the ACTS Kalman smoother track states. This is marked as TODO in the code and will be addressed once the ACTS Alignment kernel is accessible from EICrecon.
  • The residual is currently measured_u (predicted = 0 placeholder). Same fix needed.

Dependencies

Requires edm4eic >= 8.10.0 for edm4eic::AlignmentDerivativeSet (see eic/EDM4eic#161).

Related

Adds the infrastructure for a Millepede-II global alignment workflow
for the ePIC silicon tracking detectors, following the two-stage
thread-safe design:

  1. EICrecon fills edm4eic::AlignmentDerivativeSet collections (PODIO,
     thread-safe) via the new MeasurementToMille algorithm.
  2. An offline tool (scripts/podio_to_mille.py in eic/epic) converts
     the PODIO output to the binary Mille format consumed by pede.

New files:
  src/algorithms/tracking/SiliconAlignmentLabels.h
    - Defines the 12 silicon alignment layers and their Millepede label
      convention: label = layer_index * 6 + dof + 1 (1-based, 72 total)
    - buildSiliconSurfaceLabelMap() walks the ACTS TrackingGeometry at
      init time and maps GeometryIdentifier -> layer index via DD4hep
      DetElement path matching; no hardcoded geometry IDs

  src/algorithms/tracking/MeasurementToMilleConfig.h
    - Config: maxChi2PerNDF, minMomentum, fixedLayers

  src/algorithms/tracking/MeasurementToMille.{h,cc}
    - Per-event algorithm: quality-cuts tracks, iterates
      track.getMeasurements(), fills one AlignmentDerivativeSet per
      (track, silicon surface) pair
    - Local/global derivatives currently simplified (TODO: replace with
      Acts::detail::makeTrackAlignmentState() once ACTS integration is
      complete)

  src/factories/tracking/MeasurementToMille_factory.h
    - JOmniFactory wiring with PodioInput/PodioOutput and exposed
      ParameterRef entries for all config fields

  src/global/alignment/alignment.cc + CMakeLists.txt
    - New 'alignment' JANA plugin registering the factory for
      CentralCKFTracks + CentralTrackerMeasurements ->
      SiliconAlignmentDerivatives; layer 0 fixed as reference

  .codespell-ignore
    - Adds 'millepede' and 'mille' (correct names of alignment software)

Requires edm4eic >= 8.10.0 for edm4eic::AlignmentDerivativeSet.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 10, 2026 00:28
@github-actions github-actions Bot added topic: tracking Relates to tracking reconstruction topic: infrastructure labels Apr 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds an initial (PODIO-based) Millepede-II alignment derivatives production workflow for the ePIC silicon tracker by introducing a new global alignment plugin and a MeasurementToMille tracking algorithm.

Changes:

  • Add new src/global/alignment JANA plugin wiring CentralCKFTracks (+ measurements) to SiliconAlignmentDerivatives.
  • Introduce MeasurementToMille algorithm + config and silicon layer/label mapping utilities.
  • Update global CMake to build the new alignment plugin; extend codespell ignore list for “mille/millepede”.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/global/CMakeLists.txt Adds the new alignment subdirectory to the global build.
src/global/alignment/CMakeLists.txt Defines the new alignment plugin target and dependencies.
src/global/alignment/alignment.cc Registers MeasurementToMille_factory to produce SiliconAlignmentDerivatives.
src/factories/tracking/MeasurementToMille_factory.h New JOmniFactory bridging PODIO inputs/outputs to the algorithm.
src/algorithms/tracking/SiliconAlignmentLabels.h Provides silicon layer path table + (layer,DOF) Mille label scheme + surface→layer map builder.
src/algorithms/tracking/MeasurementToMilleConfig.h Adds configuration for track selection and fixed layers.
src/algorithms/tracking/MeasurementToMille.h Declares the new alignment-derivative filling algorithm.
src/algorithms/tracking/MeasurementToMille.cc Implements per-track/per-measurement derivative set filling (currently simplified placeholders).
src/algorithms/tracking/CMakeLists.txt Notes the new EDM4eic dependency requirement for the tracking algorithms library.
.codespell-ignore Adds “millepede” and “mille” to the ignore list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +25
# NOTE: The edm4eic::AlignmentDerivativeSet type is defined in the alignment
# branch of EDM4eic (~/git/EDM4eic/build-align/edm4eic/). Once it is merged
# into an official EDM4eic release and found by find_package(EDM4EIC), no extra
# include path is needed. Until then, add the build directory manually:
#
# plugin_include_directories(${PLUGIN_NAME} SYSTEM PUBLIC
# $ENV{HOME}/git/EDM4eic/build-align)

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

The NOTE block refers to a developer-local build path ($ENV{HOME}/git/EDM4eic/build-align). Committing local paths into CMake can confuse users and doesn’t enforce the required EDM4eic version at configure time. Prefer adding a proper version requirement (e.g., bump EDM4EIC_VERSION_MIN / find_package(EDM4EIC 8.10.0) for this plugin) and/or a configure-time check that errors out when AlignmentDerivativeSet isn’t available, instead of documenting a local include workaround.

Suggested change
# NOTE: The edm4eic::AlignmentDerivativeSet type is defined in the alignment
# branch of EDM4eic (~/git/EDM4eic/build-align/edm4eic/). Once it is merged
# into an official EDM4eic release and found by find_package(EDM4EIC), no extra
# include path is needed. Until then, add the build directory manually:
#
# plugin_include_directories(${PLUGIN_NAME} SYSTEM PUBLIC
# $ENV{HOME}/git/EDM4eic/build-align)
# Require an EDM4eic installation that provides AlignmentDerivativeSet via the
# package discovered by plugin_add_event_model(${PLUGIN_NAME}).
find_path(EDM4EIC_ALIGNMENT_DERIVATIVESET_INCLUDE_DIR
NAMES edm4eic/AlignmentDerivativeSet.h
HINTS ${EDM4EIC_INCLUDE_DIRS}
)
if(NOT EDM4EIC_ALIGNMENT_DERIVATIVESET_INCLUDE_DIR)
message(FATAL_ERROR
"${PLUGIN_NAME} requires an EDM4eic installation that provides "
"edm4eic/AlignmentDerivativeSet.h. Please configure against a compatible "
"EDM4eic version instead of adding a developer-local include path.")
endif()

Copilot uses AI. Check for mistakes.
// Copyright (C) 2025 ePIC Collaboration

#include <JANA/JApplication.h>
#include <JANA/JApplicationFwd.h>

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

This plugin directly includes edm4eic/AlignmentDerivativeSetCollection.h, but the repository’s CMake currently only requires EDM4eic >= 8.0. This will hard-fail compilation for users with EDM4eic < 8.10.0. Consider adding an explicit EDM4eic version guard here (like src/global/tracking/tracking.cc does with edm4eic/EDM4eicVersion.h) so the build fails with a clear message, and/or update the project-level EDM4EIC_VERSION_MIN accordingly.

Suggested change
#include <JANA/JApplicationFwd.h>
#include <JANA/JApplicationFwd.h>
#include <edm4eic/EDM4eicVersion.h>
#if EDM4EIC_VERSION_MAJOR < 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR < 10)
#error "src/global/alignment/alignment.cc requires EDM4eic >= 8.10.0 because it uses edm4eic/AlignmentDerivativeSetCollection.h"
#endif

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +40
"0-based layer indices to fix in Millepede"};

Service<AlgorithmsInit_service> m_algorithmsInit{this};

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

MeasurementToMille_factory declares Service<AlgorithmsInit_service> but does not include the header that declares AlgorithmsInit_service. Unlike src/global/tracking/tracking.cc, alignment.cc includes this factory header without previously including services/algorithms_init/AlgorithmsInit_service.h, so this will not compile. Include services/algorithms_init/AlgorithmsInit_service.h (or at least forward-declare AlgorithmsInit_service) in this header to remove include-order dependencies.

Copilot uses AI. Check for mistakes.
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));

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

residualUncert is computed as sqrt(abs(cov.xx)), which can silently turn an invalid/negative variance into a positive uncertainty and may also yield 0 for non-positive variances. It’s safer to validate the covariance element (e.g., require cov.xx > 0) and skip/log the measurement (or set a minimal floor) when it’s non-physical, so downstream Millepede weighting can’t be corrupted.

Suggested change
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);

Copilot uses AI. Check for mistakes.
plugin_link_libraries(${PLUGIN_NAME} Eigen3::Eigen particle_service_library)

# MeasurementToMille.cc uses edm4eic::AlignmentDerivativeSet, available in
# edm4eic >= 8.10.0.

Copilot AI Apr 10, 2026

Copy link

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.

Suggested change
# edm4eic >= 8.10.0.
# edm4eic >= 8.10.0.
if(DEFINED EDM4EIC_VERSION)
if(EDM4EIC_VERSION VERSION_LESS "8.10.0")
message(FATAL_ERROR
"${PLUGIN_NAME} requires edm4eic >= 8.10.0 because "
"MeasurementToMille.cc uses edm4eic::AlignmentDerivativeSet "
"(found: ${EDM4EIC_VERSION})")
endif()
else()
message(FATAL_ERROR
"${PLUGIN_NAME} requires edm4eic >= 8.10.0 because "
"MeasurementToMille.cc uses edm4eic::AlignmentDerivativeSet, "
"but EDM4EIC_VERSION is not defined so the requirement cannot be "
"verified at configure time")
endif()

Copilot uses AI. Check for mistakes.
Comment on lines +98 to +117
/// Return the Millepede base label (DOF 0 label) for a given 0-based layer index.
/// All DOF labels for this layer are [baseLabel, baseLabel + kAlignNDOF - 1].
inline int siliconAlignmentBaseLabel(int layerIndex) { return layerIndex * kAlignNDOF + 1; }

/// Return the Millepede label for a specific (layer, DOF) pair.
inline int siliconAlignmentLabel(int layerIndex, AlignmentDOF dof) {
return siliconAlignmentBaseLabel(layerIndex) + static_cast<int>(dof);
}

/// Decode a Millepede label back to (layerIndex, dof).
/// Returns std::nullopt if the label is out of range.
inline std::optional<std::pair<int, AlignmentDOF>> decodeSiliconAlignmentLabel(int label) {
if (label < 1 || label > static_cast<int>(kNSiliconLayers) * kAlignNDOF) {
return std::nullopt;
}
int zero_based = label - 1;
int layerIndex = zero_based / kAlignNDOF;
auto dof = static_cast<AlignmentDOF>(zero_based % kAlignNDOF);
return std::make_pair(layerIndex, dof);
}

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

SiliconAlignmentLabels introduces label encoding/decoding logic and the layer path table, but there are no unit tests covering boundary conditions (e.g., label 0/out-of-range, first/last valid label, round-trip encode→decode). There is existing Catch2 algorithm test infrastructure under src/tests/algorithms_test/, so adding a small test file for these pure functions would help prevent silent label-scheme regressions.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic: infrastructure topic: tracking Relates to tracking reconstruction

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants