Skip to content

feat: Demonstrator for MillePede alignment of ACTS Kalman tracks#5256

Open
goblirsc wants to merge 16 commits intoacts-project:mainfrom
goblirsc:MG_MillePede_demo
Open

feat: Demonstrator for MillePede alignment of ACTS Kalman tracks#5256
goblirsc wants to merge 16 commits intoacts-project:mainfrom
goblirsc:MG_MillePede_demo

Conversation

@goblirsc
Copy link
Copy Markdown
Contributor

@goblirsc goblirsc commented Mar 18, 2026

Add a first demonstrator of MillePede alignment, using existing ACTS Kalman tracks.

--- END COMMIT MESSAGE ---

First step towards adding actual life to the MillePede ❤️ ACTS effort!

After making Mille available as a plugin in we use it to write ACTS-Kalman tracks out into binary files that can be fit with the MillePede package.

This path is simple (as we can re-use existing tracks), but computationally highly inefficient - we have to "undo" the track fit to bring the global covariance matrix of the Kalman tracks into the format expected by Millepede.
This should hence be seen as an initial proof of concept demonstrator.
For real-life usage, the GeneralBrokenLines track refit (planned for ACTS-integration in the near future) will likely be a better choice for the fitter component in the alignment. But the final implementation should be sufficiently modular to work with any fitter nonetheless.

In this MR, we add

  • (algebraic) machinery to translate the Kalman track alignment states to what Mille expects
  • Machinery to loosely emulate how Millepede reads the Mille binaries, allowing to populate ACTS TrackAlignmentStates from Mille binaries
  • an AlignedTelescopeDetector for a simple showcase setup
  • An example algorithm using the new machinery to write (existing) Kalman tracks out into MillePede fit inputs
  • A second example algorithm that will load back the Mille files and run an alignment fit with the ACTS solver. NB: This also fixes the problem of the existing alignment example solver only "seeing" one event at a time.
  • python bindings for the above
  • A unit test ensuring that Mille writing and reading back into ACTS results in consistent alignment problems
  • Steering to run a simple workflow with single muons on a misaligned telescope detector.

The alignment fit itself is performed outside ACTS using the MillePede package. Alternatively, it is possible to read the binaries back and solve using the ACTS tooling.

@goblirsc goblirsc changed the title Feat: Demonstrator for MillePede alignment of ACTS Kalman tracks feat: Demonstrator for MillePede alignment of ACTS Kalman tracks Mar 18, 2026
@goblirsc
Copy link
Copy Markdown
Contributor Author

Wow, the CI even checks the PR title?
I failed really early this time! 😭

@github-actions github-actions bot added this to the next milestone Mar 18, 2026
@github-actions github-actions bot added Component - Examples Affects the Examples module Component - Plugins Affects one or more Plugins labels Mar 18, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 18, 2026

📊: Physics performance monitoring for b7f2833

Full contents

physmon summary

@asalzburger
Copy link
Copy Markdown
Contributor

Amazing!

@goblirsc goblirsc marked this pull request as ready for review March 20, 2026 14:55
@goblirsc
Copy link
Copy Markdown
Contributor Author

Pausing updates for now to allow for review (sorry for the big PR - once the first bits were in place the extra features just kept coming by themselves 😇 )

@goblirsc goblirsc force-pushed the MG_MillePede_demo branch from ff4d2f2 to 38176e7 Compare March 30, 2026 09:59
@goblirsc
Copy link
Copy Markdown
Contributor Author

goblirsc commented Apr 1, 2026

Could someone please review this?
image

@paulgessinger
Copy link
Copy Markdown
Member

I can have a look next week. In the meantime, maybe @pbutti or @knutzk could have a look?

@andiwand
Copy link
Copy Markdown
Contributor

andiwand commented Apr 1, 2026

It is also on my list but I didn't get to it yet. Let me also ping the review channel on MM https://mattermost.web.cern.ch/acts/channels/code-review

Copy link
Copy Markdown
Contributor

@andiwand andiwand left a comment

Choose a reason for hiding this comment

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

couple of small comments and nitpicks but didn't go through completely yet

one major change I was not sure about yet is the telescope detector modifications. I thought the alignment of our detectors is already in place and general enough to support this. I think @asalzburger knows more about this

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.

I wonder if we could get rid of Acts in the file and class name. how about MillePedeAlignmentAlgorithm?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Would prefer to keep - as this algorithm is not running Millepede but instead the ACTS-Internal alignment solver, hence the ActsSolver prefix. A MillePedeAlignmentSolver calling pede (the Millepede solver program) can / will be added later :)

Comment on lines +51 to +55
ProcessCode ActsSolverFromMille::execute(
const AlgorithmContext& /*ClangShutUp*/) const {
// this algorithm will not do anything at event-time
return ProcessCode::SUCCESS;
}
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.

if the algorithm does not do anything during execution should this rather be a "tool"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It does something during finalisation, and in terms of the workflow takes the role of an algorithm (= scheduled step of the processing pipeline), rather than being called from another algorithm. So would prefer to keep this an algorithm.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 7, 2026

/// add a tiny diagonal matrix for additional stabilisation
auto eigensolver = Eigen::SelfAdjointEigenSolver<Acts::DynamicMatrix>(
inputCov +
1.e-10 * Acts::DynamicMatrix::Identity(inputCov.rows(), inputCov.cols()));
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.

1e-10 stabilization parameter could be made configurable here?

double lambdaMax = eigenVals(eigenVals.size() - 1);
// check for a huge leading eigenvalue - this happens when the time coordinate
// is unconstrained
if (removeLargeLeading && lambdaMax > 100 * eigenVals(maxIndex - 1)) {
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.

Same comment on 1e2, perhaps configurable?

unsigned int iSurface = 0;
for (auto& [geoID, surface] : sortedGeo) {
// only consider sensitive surfaces
if (geoID.sensitive() == 0) {
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.

Suggested change
if (geoID.sensitive() == 0) {
if (!surface->isSensitive()) {

since recently we have a shorthand for this on the surface. could be nice to decouple this from the geo id

unsigned int iSurface = 0;
for (auto& [geoID, surface] : sortedGeo) {
// only consider sensitive surfaces
if (geoID.sensitive() == 0) {
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.

Suggested change
if (geoID.sensitive() == 0) {
if (!surface->isSensitive()) {

<< " +/- " << std::setw(10)
<< std::sqrt(alignResult.alignmentCovariance(row, row))
<< std::endl;
;
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.

Suggested change
;

/// and stored e.g. in a DB file for further use / validation.
/// For this initial demo, we just print them out.

std::cout << "Performed internal alignment. " << std::endl;
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.

I think these couts should be converted to use the logger


return ProcessCode::SUCCESS;
}
ProcessCode MillePedeAlignmentSandbox::finalize() {
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.

Suggested change
ProcessCode MillePedeAlignmentSandbox::finalize() {
ProcessCode MillePedeAlignmentSandbox::finalize() {

Comment on lines 180 to +188
inline const std::vector<std::unique_ptr<Acts::Transform3>>&
TelescopeDetectorElement::alignedTransforms() const {
return m_alignedTransforms;
}

inline TelescopeDetectorElement::Identifier
TelescopeDetectorElement::identifier() const {
return m_elementIdentifier;
}
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.

we could just move these one liners to the declaration

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.

ok I see - should we just make the TelescopeDetector alignable instead of adding another class?

/// @param record: Mille record to write to - should be valid pointer
/// Note: Not very efficient - we have to "un-fit" the kalman track.
/// Used for R&D, recommending the GBL track model (under development)
// for production use.
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.

Suggested change
// for production use.
/// for production use.

/// Used for R&D, recommending the GBL track model (under development)
// for production use.
void dumpToMille(const ActsAlignment::detail::TrackAlignmentState& state,
MilleRecord* record);
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.

in case record should not be nullptr we could express this by using a reference MilleRecord& record

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Component - Examples Affects the Examples module Component - Plugins Affects one or more Plugins

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants