File indexing completed on 2026-07-29 08:17:18
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "ActsAlignment/Kernel/Alignment.hpp"
0012
0013 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0014 #include "Acts/EventData/VectorTrackContainer.hpp"
0015 #include "Acts/TrackFitting/detail/KalmanGlobalCovariance.hpp"
0016 #include "Acts/Utilities/Logger.hpp"
0017 #include "Acts/Utilities/detail/EigenCompat.hpp"
0018 #include "ActsAlignment/Kernel/AlignmentError.hpp"
0019 #include "ActsAlignment/Kernel/detail/AlignmentEngine.hpp"
0020
0021 #include <queue>
0022
0023 template <typename fitter_t>
0024 template <typename source_link_t, typename fit_options_t>
0025 Acts::Result<ActsAlignment::detail::TrackAlignmentState>
0026 ActsAlignment::Alignment<fitter_t>::evaluateTrackAlignmentState(
0027 const Acts::GeometryContext& gctx,
0028 const std::vector<source_link_t>& sourceLinks,
0029 const Acts::BoundTrackParameters& sParameters,
0030 const fit_options_t& fitOptions,
0031 const std::unordered_map<const Acts::Surface*, std::size_t>&
0032 idxedAlignSurfaces,
0033 const ActsAlignment::AlignmentMask& alignMask) const {
0034 Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0035 Acts::VectorMultiTrajectory{}};
0036
0037
0038 Acts::SourceLinkAdapterIterator begin{sourceLinks.begin()};
0039 Acts::SourceLinkAdapterIterator end{sourceLinks.end()};
0040
0041
0042 auto fitRes = m_fitter.fit(begin, end, sParameters, fitOptions, tracks);
0043
0044 if (!fitRes.ok()) {
0045 ACTS_WARNING("Fit failure");
0046 return fitRes.error();
0047 }
0048
0049 const auto& track = fitRes.value();
0050
0051 const auto& globalTrackParamsCov =
0052 Acts::detail::globalTrackParametersCovariance(
0053 tracks.trackStateContainer(), track.tipIndex());
0054
0055 const auto alignState = detail::trackAlignmentState(
0056 gctx, tracks.trackStateContainer(), track.tipIndex(),
0057 globalTrackParamsCov, idxedAlignSurfaces, alignMask);
0058 if (alignState.alignmentDof == 0) {
0059 ACTS_VERBOSE("No alignment dof on track!");
0060 return AlignmentError::NoAlignmentDofOnTrack;
0061 }
0062 return alignState;
0063 }
0064
0065 template <typename fitter_t>
0066 template <typename trajectory_container_t,
0067 typename start_parameters_container_t, typename fit_options_t>
0068 void ActsAlignment::Alignment<fitter_t>::calculateAlignmentParameters(
0069 const trajectory_container_t& trajectoryCollection,
0070 const start_parameters_container_t& startParametersCollection,
0071 const fit_options_t& fitOptions,
0072 ActsAlignment::AlignmentResult& alignResult,
0073 const ActsAlignment::AlignmentMask& alignMask) const {
0074
0075
0076 assert(trajectoryCollection.size() == startParametersCollection.size());
0077
0078
0079 alignResult.alignmentDof =
0080 alignResult.idxedAlignSurfaces.size() * Acts::eAlignmentSize;
0081
0082 fit_options_t fitOptionsWithRefSurface = fitOptions;
0083
0084
0085 alignResult.chi2 = 0;
0086 alignResult.measurementDim = 0;
0087 alignResult.numTracks = trajectoryCollection.size();
0088 std::vector<detail::TrackAlignmentState> alignmentStates;
0089 for (unsigned int iTraj = 0; iTraj < trajectoryCollection.size(); iTraj++) {
0090 const auto& sourceLinks = trajectoryCollection.at(iTraj);
0091 const auto& sParameters = startParametersCollection.at(iTraj);
0092
0093 fitOptionsWithRefSurface.referenceSurface = &sParameters.referenceSurface();
0094
0095 auto evaluateRes = evaluateTrackAlignmentState(
0096 fitOptions.geoContext, sourceLinks, sParameters,
0097 fitOptionsWithRefSurface, alignResult.idxedAlignSurfaces, alignMask);
0098 if (!evaluateRes.ok()) {
0099 ACTS_DEBUG("Evaluation of alignment state for track " << iTraj
0100 << " failed");
0101 continue;
0102 }
0103 const auto& alignState = evaluateRes.value();
0104 alignmentStates.push_back(alignState);
0105 }
0106 return calculateAlignmentParameters(alignmentStates, alignResult);
0107 }
0108
0109 template <typename fitter_t>
0110 void ActsAlignment::Alignment<fitter_t>::calculateAlignmentParameters(
0111 const std::vector<detail::TrackAlignmentState>& trackAlignmentStates,
0112 AlignmentResult& alignResult) const {
0113
0114
0115 detail::solveAlignmentParameters(trackAlignmentStates, alignResult, logger());
0116 }
0117
0118 template <typename fitter_t>
0119 double ActsAlignment::Alignment<fitter_t>::decompositionAnalysis(
0120 const AlignmentResult& res, std::ostream& out) {
0121 if (res.sumChi2SecondDerivative.cols() == 0) {
0122 ACTS_ERROR(
0123 "Please run Alignment::calculateAlignmentParameters before calling "
0124 "Alignment::decompositionAnalysis.");
0125 return -1;
0126 }
0127 Eigen::SelfAdjointEigenSolver<Acts::DynamicMatrix> eigenSolver(
0128 res.sumChi2SecondDerivative);
0129 if (eigenSolver.info() != Eigen::Success) {
0130 std::cout << " FAILED to find decompose correlation term" << std::endl;
0131 return -1;
0132 }
0133 const Acts::DynamicVector eigenVals = eigenSolver.eigenvalues();
0134 const Acts::DynamicMatrix eigenVecs = eigenSolver.eigenvectors();
0135
0136 std::map<double, int> sortedEV;
0137 for (int k = 0; k < eigenVals.size(); ++k) {
0138 sortedEV.emplace(eigenVals(k), k);
0139 }
0140 double firstEV = -1, lastEV = -1;
0141 for (auto& [EV, index] : sortedEV) {
0142 if (EV > 0 && firstEV < 0) {
0143 firstEV = EV;
0144 }
0145 lastEV = EV;
0146 out << " Eigenvector " << index << " has eigenvalue " << EV << std::endl;
0147 for (Eigen::Index row = 0; row < eigenVecs.rows(); ++row) {
0148 out << " " << std::setw(12) << " " << std::setw(3) << row + 1
0149 << " " << std::setw(12) << eigenVecs(row, index) << std::endl;
0150 }
0151 out << std::endl;
0152 }
0153 return lastEV / firstEV;
0154 }
0155
0156 template <typename fitter_t>
0157 Acts::Result<void>
0158 ActsAlignment::Alignment<fitter_t>::updateAlignmentParameters(
0159 const Acts::GeometryContext& gctx,
0160 const std::vector<Acts::SurfacePlacementBase*>& alignedDetElements,
0161 const ActsAlignment::AlignedTransformUpdaterConcept auto&
0162 alignedTransformUpdater,
0163 ActsAlignment::AlignmentResult& alignResult) const {
0164
0165 Acts::AlignmentVector deltaAlignmentParam = Acts::AlignmentVector::Zero();
0166 for (const auto& [surface, index] : alignResult.idxedAlignSurfaces) {
0167
0168 const Acts::Vector3& oldCenter = surface->center(gctx);
0169 const Acts::Transform3& oldTransform =
0170 surface->localToGlobalTransform(gctx);
0171
0172
0173 deltaAlignmentParam = alignResult.deltaAlignmentParameters.segment(
0174 Acts::eAlignmentSize * index, Acts::eAlignmentSize);
0175
0176 Acts::Vector3 deltaCenter =
0177 deltaAlignmentParam.segment<3>(Acts::eAlignmentCenter0);
0178
0179 Acts::Vector3 deltaEulerAngles =
0180 deltaAlignmentParam.segment<3>(Acts::eAlignmentRotation0);
0181
0182
0183 const Acts::Vector3 newCenter = oldCenter + deltaCenter;
0184 Acts::Transform3 newTransform = oldTransform;
0185 newTransform.translation() = newCenter;
0186
0187
0188
0189 newTransform *=
0190 Acts::AngleAxis3(deltaEulerAngles(2), Acts::Vector3::UnitZ());
0191 newTransform *=
0192 Acts::AngleAxis3(deltaEulerAngles(1), Acts::Vector3::UnitY());
0193 newTransform *=
0194 Acts::AngleAxis3(deltaEulerAngles(0), Acts::Vector3::UnitX());
0195
0196
0197
0198
0199 ACTS_VERBOSE("Delta of alignment parameters at element "
0200 << index << "= \n"
0201 << deltaAlignmentParam);
0202 bool updated = alignedTransformUpdater(alignedDetElements.at(index), gctx,
0203 newTransform);
0204 if (!updated) {
0205 ACTS_ERROR("Update alignment parameters for detector element failed");
0206 return AlignmentError::AlignmentParametersUpdateFailure;
0207 }
0208 }
0209
0210 return Acts::Result<void>::success();
0211 }
0212
0213 template <typename fitter_t>
0214 template <typename trajectory_container_t,
0215 typename start_parameters_container_t, typename fit_options_t>
0216 Acts::Result<ActsAlignment::AlignmentResult>
0217 ActsAlignment::Alignment<fitter_t>::align(
0218 const trajectory_container_t& trajectoryCollection,
0219 const start_parameters_container_t& startParametersCollection,
0220 const ActsAlignment::AlignmentOptions<fit_options_t>& alignOptions) const {
0221
0222 AlignmentResult alignResult;
0223
0224
0225 for (unsigned int iDetElement = 0;
0226 iDetElement < alignOptions.alignedDetElements.size(); iDetElement++) {
0227 alignResult.idxedAlignSurfaces.emplace(
0228 &alignOptions.alignedDetElements.at(iDetElement)->surface(),
0229 iDetElement);
0230 }
0231 ACTS_VERBOSE("There are " << alignResult.idxedAlignSurfaces.size()
0232 << " detector elements to be aligned");
0233
0234
0235 bool converged = false;
0236 bool alignmentParametersUpdated = false;
0237 std::queue<double> recentChi2ONdf;
0238 ACTS_INFO("Max number of iterations: " << alignOptions.maxIterations);
0239 for (unsigned int iIter = 0; iIter < alignOptions.maxIterations; iIter++) {
0240
0241
0242 AlignmentMask alignMask = AlignmentMask::All;
0243
0244 auto iter_it = alignOptions.iterationState.find(iIter);
0245 if (iter_it != alignOptions.iterationState.end()) {
0246 alignMask = iter_it->second;
0247 }
0248
0249 calculateAlignmentParameters(
0250 trajectoryCollection, startParametersCollection,
0251 alignOptions.fitOptions, alignResult, alignMask);
0252
0253 ACTS_INFO("iIter = " << iIter << ", total chi2 = " << alignResult.chi2
0254 << ", total measurementDim = "
0255 << alignResult.measurementDim
0256 << " and average chi2/ndf = "
0257 << alignResult.averageChi2ONdf);
0258
0259
0260
0261 if (recentChi2ONdf.size() >=
0262 alignOptions.deltaAverageChi2ONdfCutOff.first) {
0263 if (std::abs(recentChi2ONdf.front() - alignResult.averageChi2ONdf) <=
0264 alignOptions.deltaAverageChi2ONdfCutOff.second) {
0265 ACTS_INFO(
0266 "Alignment has converged with change of chi2/ndf < "
0267 << alignOptions.deltaAverageChi2ONdfCutOff.second << " in the last "
0268 << alignOptions.deltaAverageChi2ONdfCutOff.first << " iterations"
0269 << " after " << iIter << " iteration(s)");
0270 converged = true;
0271 break;
0272 }
0273 recentChi2ONdf.pop();
0274 }
0275
0276 if (alignResult.averageChi2ONdf <= alignOptions.averageChi2ONdfCutOff) {
0277 ACTS_INFO("Alignment has converged with average chi2/ndf < "
0278 << alignOptions.averageChi2ONdfCutOff << " after " << iIter
0279 << " iteration(s)");
0280 converged = true;
0281 break;
0282 }
0283
0284
0285 recentChi2ONdf.push(alignResult.averageChi2ONdf);
0286
0287 ACTS_INFO("The solved delta of alignmentParameters = \n "
0288 << alignResult.deltaAlignmentParameters);
0289
0290 auto updateRes = updateAlignmentParameters(
0291 alignOptions.fitOptions.geoContext, alignOptions.alignedDetElements,
0292 alignOptions.alignedTransformUpdater, alignResult);
0293 if (!updateRes.ok()) {
0294 ACTS_ERROR("Update alignment parameters failed: " << updateRes.error());
0295 return updateRes.error();
0296 }
0297 alignmentParametersUpdated = true;
0298 }
0299
0300
0301 if (!converged) {
0302 ACTS_ERROR("Alignment is not converged.");
0303 alignResult.result = AlignmentError::ConvergeFailure;
0304 }
0305
0306
0307
0308 if (alignmentParametersUpdated) {
0309 for (const auto& det : alignOptions.alignedDetElements) {
0310 const auto& surface = &det->surface();
0311 const auto& transform =
0312 det->localToGlobalTransform(alignOptions.fitOptions.geoContext);
0313
0314 alignResult.alignedParameters.emplace(det, transform);
0315 const auto& translation = transform.translation();
0316 const auto& rotation = transform.rotation();
0317 const Acts::Vector3 rotAngles =
0318 Acts::detail::EigenCompat::canonicalEulerAngles(rotation, 2, 1, 0);
0319 ACTS_VERBOSE("Detector element with surface "
0320 << surface->geometryId()
0321 << " has aligned geometry position as below:");
0322 ACTS_VERBOSE("Center (cenX, cenY, cenZ) = " << translation.transpose());
0323 ACTS_VERBOSE(
0324 "Euler angles (rotZ, rotY, rotX) = " << rotAngles.transpose());
0325 ACTS_VERBOSE("Rotation matrix = \n" << rotation);
0326 }
0327 } else {
0328 ACTS_DEBUG("Alignment parameters is not updated.");
0329 }
0330
0331 return alignResult;
0332 }