File indexing completed on 2025-12-16 09:23:17
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Seeding/detail/FastStrawLineFitter.hpp"
0010
0011 #include "Acts/Definitions/Units.hpp"
0012 #include "Acts/Utilities/StringHelpers.hpp"
0013 #include "Acts/Utilities/detail/periodic.hpp"
0014
0015 #include <format>
0016
0017 using namespace Acts::UnitLiterals;
0018
0019 namespace {
0020
0021
0022 constexpr double inDeg(const double x) {
0023 return x / 1._degree;
0024 }
0025
0026 constexpr double inNanoS(const double x) {
0027 return x / 1._ns;
0028 }
0029 std::string printThetaStep(const double theta, const double thetaPrime,
0030 const double thetaTwoPrime) {
0031 return std::format(
0032 "-- theta: {:.3f}, thetaPrime: {:.3f}, thetaTwoPrime: {:.3f} --> "
0033 "step-size: {:.8f}",
0034 inDeg(theta), inDeg(thetaPrime), inDeg(thetaTwoPrime),
0035 inDeg(thetaPrime / thetaTwoPrime));
0036 }
0037 }
0038 namespace Acts::Experimental::detail {
0039
0040 void FastStrawLineFitter::FitAuxiliaries::print(std::ostream& ostr) const {
0041 ostr << std::format("T_zzyy: {:.3f}, ", T_zzyy)
0042 << std::format("T_yz: {:.3f}, ", T_yz)
0043 << std::format("T_rz: {:.3f}, ", T_rz)
0044 << std::format("T_ry: {:.3f}, ", T_ry)
0045 << std::format("centre ( {:.3f}, {:3f}), ", centerY, centerZ)
0046 << std::format("y0: {:.3f}", fitY0)
0047 << std::format(", norm: {:.3f}", covNorm) << ", nDoF: " << nDoF;
0048 }
0049 void FastStrawLineFitter::FitAuxiliariesWithT0 ::print(
0050 std::ostream& ostr) const {
0051 ostr << std::format("R_vr: {:.3f}, ", R_vr);
0052 ostr << std::format("R_ar: {:.3f}, ", R_ar);
0053 ostr << std::format("R_vv: {:.3f}, ", R_vv);
0054 ostr << std::format("R_a: {:.3f}, ", R_a);
0055 ostr << std::format("R_v: {:.3f}, ", R_v);
0056 ostr << std::format("T_vz: {:.3f}, ", T_vz);
0057 ostr << std::format("T_vy: {:.3f}, ", T_vy);
0058 ostr << std::format("T_az: {:.3f}, ", T_az);
0059 ostr << std::format("T_ay: {:.3f}, ", T_ay);
0060 FitAuxiliaries::print(ostr);
0061 }
0062
0063 void FastStrawLineFitter::FitResult::print(std::ostream& ostr) const {
0064 ostr << "# iteration: " << nIter << ", nDoF: " << nDoF << ", chi2: " << chi2
0065 << ", chi2 / nDoF: " << (chi2 / static_cast<double>(nDoF)) << ",\n";
0066 ostr << std::format("theta: {:.3f} pm {:.3f}, ", inDeg(theta), inDeg(dTheta))
0067 << std::format("y0: {:.3f} pm {:.3f}", y0, dY0);
0068 }
0069 void FastStrawLineFitter::FitResultT0::print(std::ostream& ostr) const {
0070 FitResult::print(ostr);
0071 ostr << std::format(", t0: {:.3f} pm {:.3f}", inNanoS(t0), inNanoS(dT0));
0072 }
0073
0074 using Vector = FastStrawLineFitter::Vector;
0075 const Acts::Logger& FastStrawLineFitter::logger() const {
0076 assert(m_logger != nullptr);
0077 return *m_logger;
0078 }
0079 FastStrawLineFitter::FastStrawLineFitter(const Config& cfg,
0080 std::unique_ptr<const Logger> logger)
0081 : m_cfg{cfg}, m_logger{std::move(logger)} {}
0082
0083 inline void FastStrawLineFitter::calcAngularDerivatives(
0084 const TrigonomHelper& angles, const FitAuxiliaries& fitPars,
0085 double& thetaPrime, double& thetaTwoPrime) const {
0086 thetaPrime = 0.5 * fitPars.T_zzyy * angles.sinTwoTheta -
0087 fitPars.T_yz * angles.cosTwoTheta -
0088 fitPars.T_rz * angles.cosTheta - fitPars.T_ry * angles.sinTheta;
0089 thetaTwoPrime = fitPars.T_zzyy * angles.cosTwoTheta +
0090 2. * fitPars.T_yz * angles.sinTwoTheta +
0091 fitPars.T_rz * angles.sinTheta -
0092 fitPars.T_ry * angles.cosTheta;
0093 }
0094 double FastStrawLineFitter::startTheta(const FitAuxiliaries& fitPars) {
0095 double thetaGuess =
0096 std::atan2(2. * (fitPars.T_yz - fitPars.T_ry), fitPars.T_zzyy) / 2.;
0097 return Acts::detail::wrap_periodic(thetaGuess, 0., std::numbers::pi);
0098 }
0099 double FastStrawLineFitter::calcTimeGrad(const TrigonomHelper& angles,
0100 const FitAuxiliariesWithT0& pars) {
0101 return pars.fitY0 * pars.R_v - pars.R_vr + pars.T_vz * angles.sinTheta -
0102 pars.T_vy * angles.cosTheta;
0103 }
0104
0105 void FastStrawLineFitter::completeResult(const FitAuxiliaries& fitPars,
0106 const double thetaTwoPrime,
0107 FitResult& result) const {
0108 result.dTheta = std::sqrt(1. / Acts::abs(thetaTwoPrime));
0109 const double tanTheta = std::tan(result.theta);
0110 const double secTheta = 1. / std::cos(result.theta);
0111 result.y0 =
0112 fitPars.centerY - fitPars.centerZ * tanTheta + fitPars.fitY0 * secTheta;
0113 result.dY0 = Acts::fastHypot(-fitPars.centerZ * Acts::square(secTheta) +
0114 fitPars.fitY0 * secTheta * tanTheta,
0115 secTheta);
0116
0117 result.dY0 *= result.dTheta;
0118 ACTS_DEBUG(__func__ << "() - " << __LINE__ << ": Fit succeeded " << result);
0119 }
0120
0121 std::optional<FastStrawLineFitter::FitResult> FastStrawLineFitter::fit(
0122 const FitAuxiliaries& fitPars) const {
0123
0124 if (fitPars.nDoF == 0u) {
0125 return std::nullopt;
0126 }
0127 ACTS_DEBUG(__func__ << "() - " << __LINE__ << ": Input fit parameters "
0128 << fitPars);
0129
0130 const double thetaGuess = startTheta(fitPars);
0131 ACTS_DEBUG(__func__ << "() - " << __LINE__
0132 << ": Start fast fit seed theta: " << inDeg(thetaGuess));
0133
0134 FitResult result{};
0135 result.theta = thetaGuess;
0136 result.nDoF = fitPars.nDoF;
0137
0138 double thetaPrime{0.};
0139 double thetaTwoPrime{0.};
0140 while (result.nIter <= m_cfg.maxIter) {
0141 ++result.nIter;
0142 const TrigonomHelper angles{result.theta};
0143 calcAngularDerivatives(angles, fitPars, thetaPrime, thetaTwoPrime);
0144 if (std::abs(thetaTwoPrime) < 2. * std::numeric_limits<double>::epsilon()) {
0145 ACTS_WARNING(__func__
0146 << "() - " << __LINE__
0147 << ": The fast straw fit encountered a singular "
0148 "second derivative "
0149 << fitPars << ", \n"
0150 << printThetaStep(result.theta, thetaPrime, thetaTwoPrime)
0151 << "\n"
0152 << result);
0153 return std::nullopt;
0154 }
0155 const double update = thetaPrime / thetaTwoPrime;
0156 ACTS_VERBOSE(
0157 __func__ << "() - " << __LINE__ << ": Fit iteration #" << result.nIter
0158 << " "
0159 << printThetaStep(result.theta, thetaPrime, thetaTwoPrime));
0160
0161 if (std::abs(update) < m_cfg.precCutOff) {
0162 completeResult(fitPars, thetaTwoPrime, result);
0163 return result;
0164 }
0165 result.theta -= update;
0166 }
0167
0168 ACTS_WARNING(
0169 __func__ << "() - " << __LINE__
0170 << ": The fast straw fit did not converge " << fitPars << ", \n"
0171 << printThetaStep(result.theta, thetaPrime, thetaTwoPrime)
0172 << "\n"
0173 << result);
0174 return std::nullopt;
0175 }
0176
0177 FastStrawLineFitter::UpdateStatus FastStrawLineFitter::updateIteration(
0178 const FitAuxiliariesWithT0& fitPars, FitResultT0& fitResult) const {
0179 ++fitResult.nIter;
0180 if (fitResult.nIter > m_cfg.maxIter) {
0181 ACTS_WARNING(__func__ << "() - " << __LINE__
0182 << ": The fast straw fit did not converge " << fitPars
0183 << "\n"
0184 << fitResult);
0185 return UpdateStatus::Exceeded;
0186 }
0187
0188 ActsSquareMatrix<2> cov{ActsSquareMatrix<2>::Zero()};
0189 Vector2 grad{Vector2::Zero()};
0190
0191 const TrigonomHelper angles{fitResult.theta};
0192
0193 calcAngularDerivatives(angles, fitPars, grad[0], cov(0, 0));
0194 grad[1] = calcTimeGrad(angles, fitPars);
0195 if (grad.norm() < m_cfg.precCutOff) {
0196 completeResult(fitPars, cov(0, 0), fitResult);
0197 ACTS_DEBUG(__func__ << "() - " << __LINE__ << ": Fit converged "
0198 << fitResult);
0199 return UpdateStatus::Converged;
0200 }
0201
0202 cov(1, 0) = cov(0, 1) =
0203 fitPars.T_vz * angles.cosTheta + fitPars.T_vy * angles.sinTheta;
0204
0205 cov(1, 1) = -fitPars.R_v * fitPars.R_v * fitPars.covNorm -
0206 fitPars.fitY0 * fitPars.R_a + fitPars.R_vv + fitPars.R_ar -
0207 (fitPars.T_az * angles.sinTheta - fitPars.T_ay * angles.cosTheta);
0208 ACTS_VERBOSE(
0209 __func__
0210 << "() - " << __LINE__
0211 << ": -fitPars.R_v * fitPars.R_v * fitPars.covNorm + fitPars.R_vv: "
0212 << (-fitPars.R_v * fitPars.R_v * fitPars.covNorm + fitPars.R_vv)
0213 << ", fitPars.fitY0 * fitPars.R_a: " << (fitPars.fitY0 * fitPars.R_a)
0214 << ", fitPars.R_ar: " << fitPars.R_ar
0215 << ", fitPars.T_az * angles.sinTheta - fitPars.T_ay * angles.cosTheta: "
0216 << (fitPars.T_az * angles.sinTheta - fitPars.T_ay * angles.cosTheta));
0217 const auto invCov = cov.inverse();
0218 if (invCov(1, 1) < 0) {
0219 ACTS_WARNING("Invalid covariance\n"
0220 << cov << ", determinant: " << cov.determinant() << ", "
0221 << fitPars);
0222 return UpdateStatus::Exceeded;
0223 }
0224 const Vector2 update = invCov * grad;
0225
0226 ACTS_VERBOSE(__func__ << "() - " << __LINE__ << " intermediate result "
0227 << fitResult << "\n"
0228 << std::format("gradient: ({:.3f}, {:.3f})",
0229 inDeg(grad[0]), inNanoS(grad[1]))
0230 << ", covariance:" << std::endl
0231 << toString(cov) << std::endl
0232 << cov.determinant()
0233 << std::format(" update: ({:.3f}, {:.3f}).",
0234 inDeg(update[0]), inNanoS(update[1])));
0235
0236 if (update.norm() < m_cfg.precCutOff) {
0237 completeResult(fitPars, cov(0, 0), fitResult);
0238 ACTS_DEBUG(__func__ << "() - " << __LINE__ << ": Fit converged "
0239 << fitResult);
0240 return UpdateStatus::Converged;
0241 }
0242 fitResult.t0 -= update[1];
0243 fitResult.theta -= update[0];
0244 fitResult.dT0 = std::sqrt(1. / cov(1, 1));
0245 completeResult(fitPars, cov(0, 0), fitResult);
0246
0247 return UpdateStatus::GoodStep;
0248 }
0249 }