File indexing completed on 2025-07-11 07:49:40
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
0016 template <typename fitter_t>
0017 template <typename source_link_t, typename start_parameters_t,
0018 typename fit_options_t>
0019 Acts::Result<ActsAlignment::detail::TrackAlignmentState>
0020 ActsAlignment::Alignment<fitter_t>::evaluateTrackAlignmentState(
0021 const Acts::GeometryContext& gctx,
0022 const std::vector<source_link_t>& sourceLinks,
0023 const start_parameters_t& sParameters, const fit_options_t& fitOptions,
0024 const std::unordered_map<const Acts::Surface*, std::size_t>&
0025 idxedAlignSurfaces,
0026 const ActsAlignment::AlignmentMask& alignMask) const {
0027 Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0028 Acts::VectorMultiTrajectory{}};
0029
0030
0031 Acts::SourceLinkAdapterIterator begin{sourceLinks.begin()};
0032 Acts::SourceLinkAdapterIterator end{sourceLinks.end()};
0033
0034
0035 auto fitRes = m_fitter.fit(begin, end, sParameters, fitOptions, tracks);
0036
0037 if (!fitRes.ok()) {
0038 ACTS_WARNING("Fit failure");
0039 return fitRes.error();
0040 }
0041
0042 const auto& track = fitRes.value();
0043
0044 const auto& globalTrackParamsCov =
0045 Acts::detail::globalTrackParametersCovariance(
0046 tracks.trackStateContainer(), track.tipIndex());
0047
0048 const auto alignState = detail::trackAlignmentState(
0049 gctx, tracks.trackStateContainer(), track.tipIndex(),
0050 globalTrackParamsCov, idxedAlignSurfaces, alignMask);
0051 if (alignState.alignmentDof == 0) {
0052 ACTS_VERBOSE("No alignment dof on track!");
0053 return AlignmentError::NoAlignmentDofOnTrack;
0054 }
0055 return alignState;
0056 }
0057
0058 template <typename fitter_t>
0059 template <typename trajectory_container_t,
0060 typename start_parameters_container_t, typename fit_options_t>
0061 void ActsAlignment::Alignment<fitter_t>::calculateAlignmentParameters(
0062 const trajectory_container_t& trajectoryCollection,
0063 const start_parameters_container_t& startParametersCollection,
0064 const fit_options_t& fitOptions,
0065 ActsAlignment::AlignmentResult& alignResult,
0066 const ActsAlignment::AlignmentMask& alignMask) const {
0067
0068
0069 assert(trajectoryCollection.size() == startParametersCollection.size());
0070
0071
0072 alignResult.alignmentDof =
0073 alignResult.idxedAlignSurfaces.size() * Acts::eAlignmentSize;
0074
0075 Acts::ActsDynamicVector sumChi2Derivative =
0076 Acts::ActsDynamicVector::Zero(alignResult.alignmentDof);
0077 Acts::ActsDynamicMatrix sumChi2SecondDerivative =
0078 Acts::ActsDynamicMatrix::Zero(alignResult.alignmentDof,
0079 alignResult.alignmentDof);
0080
0081 fit_options_t fitOptionsWithRefSurface = fitOptions;
0082
0083
0084 alignResult.chi2 = 0;
0085 alignResult.measurementDim = 0;
0086 alignResult.numTracks = trajectoryCollection.size();
0087 double sumChi2ONdf = 0;
0088 for (unsigned int iTraj = 0; iTraj < trajectoryCollection.size(); iTraj++) {
0089 const auto& sourceLinks = trajectoryCollection.at(iTraj);
0090 const auto& sParameters = startParametersCollection.at(iTraj);
0091
0092 fitOptionsWithRefSurface.referenceSurface = &sParameters.referenceSurface();
0093
0094 auto evaluateRes = evaluateTrackAlignmentState(
0095 fitOptions.geoContext, sourceLinks, sParameters,
0096 fitOptionsWithRefSurface, alignResult.idxedAlignSurfaces, alignMask);
0097 if (!evaluateRes.ok()) {
0098 ACTS_DEBUG("Evaluation of alignment state for track " << iTraj
0099 << " failed");
0100 continue;
0101 }
0102 const auto& alignState = evaluateRes.value();
0103 for (const auto& [rowSurface, rows] : alignState.alignedSurfaces) {
0104 const auto& [dstRow, srcRow] = rows;
0105
0106 sumChi2Derivative.segment<Acts::eAlignmentSize>(dstRow *
0107 Acts::eAlignmentSize) +=
0108 alignState.alignmentToChi2Derivative.segment(
0109 srcRow * Acts::eAlignmentSize, Acts::eAlignmentSize);
0110
0111 for (const auto& [colSurface, cols] : alignState.alignedSurfaces) {
0112 const auto& [dstCol, srcCol] = cols;
0113 sumChi2SecondDerivative
0114 .block<Acts::eAlignmentSize, Acts::eAlignmentSize>(
0115 dstRow * Acts::eAlignmentSize, dstCol * Acts::eAlignmentSize) +=
0116 alignState.alignmentToChi2SecondDerivative.block(
0117 srcRow * Acts::eAlignmentSize, srcCol * Acts::eAlignmentSize,
0118 Acts::eAlignmentSize, Acts::eAlignmentSize);
0119 }
0120 }
0121 alignResult.chi2 += alignState.chi2;
0122 alignResult.measurementDim += alignState.measurementDim;
0123 sumChi2ONdf += alignState.chi2 / alignState.measurementDim;
0124 }
0125 alignResult.averageChi2ONdf = sumChi2ONdf / alignResult.numTracks;
0126
0127
0128
0129
0130 std::size_t alignDof = alignResult.alignmentDof;
0131 Acts::ActsDynamicMatrix sumChi2SecondDerivativeInverse =
0132 Acts::ActsDynamicMatrix::Zero(alignDof, alignDof);
0133 sumChi2SecondDerivativeInverse = sumChi2SecondDerivative.inverse();
0134 if (sumChi2SecondDerivativeInverse.hasNaN()) {
0135 ACTS_DEBUG("Chi2 second derivative inverse has NaN");
0136
0137 }
0138
0139
0140 alignResult.deltaAlignmentParameters =
0141 Acts::ActsDynamicVector::Zero(alignDof);
0142 alignResult.alignmentCovariance =
0143 Acts::ActsDynamicMatrix::Zero(alignDof, alignDof);
0144
0145 alignResult.deltaAlignmentParameters =
0146 -sumChi2SecondDerivative.fullPivLu().solve(sumChi2Derivative);
0147 ACTS_VERBOSE("sumChi2SecondDerivative = \n" << sumChi2SecondDerivative);
0148 ACTS_VERBOSE("sumChi2Derivative = \n" << sumChi2Derivative);
0149 ACTS_VERBOSE("alignResult.deltaAlignmentParameters \n");
0150
0151
0152 alignResult.alignmentCovariance = 2 * sumChi2SecondDerivativeInverse;
0153
0154 alignResult.deltaChi2 = 0.5 * sumChi2Derivative.transpose() *
0155 alignResult.deltaAlignmentParameters;
0156 }
0157
0158 template <typename fitter_t>
0159 Acts::Result<void>
0160 ActsAlignment::Alignment<fitter_t>::updateAlignmentParameters(
0161 const Acts::GeometryContext& gctx,
0162 const std::vector<Acts::DetectorElementBase*>& alignedDetElements,
0163 const ActsAlignment::AlignedTransformUpdater& alignedTransformUpdater,
0164 ActsAlignment::AlignmentResult& alignResult) const {
0165
0166 Acts::AlignmentVector deltaAlignmentParam = Acts::AlignmentVector::Zero();
0167 for (const auto& [surface, index] : alignResult.idxedAlignSurfaces) {
0168
0169 const Acts::Vector3& oldCenter = surface->center(gctx);
0170 const Acts::Transform3& oldTransform = surface->transform(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->transform(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 = rotation.eulerAngles(2, 1, 0);
0318 ACTS_VERBOSE("Detector element with surface "
0319 << surface->geometryId()
0320 << " has aligned geometry position as below:");
0321 ACTS_VERBOSE("Center (cenX, cenY, cenZ) = " << translation.transpose());
0322 ACTS_VERBOSE(
0323 "Euler angles (rotZ, rotY, rotX) = " << rotAngles.transpose());
0324 ACTS_VERBOSE("Rotation matrix = \n" << rotation);
0325 }
0326 } else {
0327 ACTS_DEBUG("Alignment parameters is not updated.");
0328 }
0329
0330 return alignResult;
0331 }