File indexing completed on 2026-05-26 07:35:02
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Validation/ResPlotTool.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/TrackParametrization.hpp"
0013 #include "Acts/Surfaces/Surface.hpp"
0014 #include "Acts/Utilities/Intersection.hpp"
0015 #include "Acts/Utilities/Result.hpp"
0016
0017 #include <cmath>
0018 #include <format>
0019
0020 namespace ActsExamples {
0021
0022 static constexpr double nan = std::numeric_limits<double>::quiet_NaN();
0023
0024 ResPlotTool::ResPlotTool(const ResPlotTool::Config& cfg,
0025 Acts::Logging::Level lvl)
0026 : m_cfg(cfg), m_logger(Acts::getDefaultLogger("ResPlotTool", lvl)) {
0027 const auto& etaAxis = m_cfg.varBinning.at("Eta");
0028 const auto& phiAxis = m_cfg.varBinning.at("Phi");
0029 const auto& ptAxis = m_cfg.varBinning.at("Pt");
0030 const auto& pullAxis = m_cfg.varBinning.at("Pull");
0031
0032 ACTS_DEBUG("Initialize the histograms for residual and pull plots");
0033
0034 std::vector<std::string> allParamNames = m_cfg.paramNames;
0035 allParamNames.push_back(m_cfg.qOverPtName);
0036 allParamNames.push_back(m_cfg.relQoverPtName);
0037
0038 for (const std::string& parName : allParamNames) {
0039 const std::string parResidual = "Residual_" + parName;
0040 const auto& residualAxis = m_cfg.varBinning.at(parResidual);
0041
0042
0043 m_res.emplace(parName, Acts::Experimental::Histogram1(
0044 std::format("res_{}", parName),
0045 std::format("Residual of {}", parName),
0046 std::array{residualAxis}));
0047
0048
0049 m_resVsEta.emplace(parName,
0050 Acts::Experimental::Histogram2(
0051 std::format("res_{}_vs_eta", parName),
0052 std::format("Residual of {} vs eta", parName),
0053 std::array{etaAxis, residualAxis}));
0054
0055
0056 m_resVsPt.emplace(parName, Acts::Experimental::Histogram2(
0057 std::format("res_{}_vs_pT", parName),
0058 std::format("Residual of {} vs pT", parName),
0059 std::array{ptAxis, residualAxis}));
0060
0061
0062 m_resVsEtaPhi.emplace(parName,
0063 Acts::Experimental::Histogram3(
0064 std::format("res_{}_vs_eta_phi", parName),
0065 std::format("Residual of {} vs eta-phi", parName),
0066 std::array{etaAxis, phiAxis, residualAxis}));
0067
0068
0069 m_resVsEtaPt.emplace(parName,
0070 Acts::Experimental::Histogram3(
0071 std::format("res_{}_vs_eta_pT", parName),
0072 std::format("Residual of {} vs eta-pT", parName),
0073 std::array{etaAxis, ptAxis, residualAxis}));
0074
0075
0076 m_pull.emplace(
0077 parName, Acts::Experimental::Histogram1(
0078 std::format("pull_{}", parName),
0079 std::format("Pull of {}", parName), std::array{pullAxis}));
0080
0081
0082 m_pullVsEta.emplace(parName, Acts::Experimental::Histogram2(
0083 std::format("pull_{}_vs_eta", parName),
0084 std::format("Pull of {} vs eta", parName),
0085 std::array{etaAxis, pullAxis}));
0086
0087
0088 m_pullVsPt.emplace(parName, Acts::Experimental::Histogram2(
0089 std::format("pull_{}_vs_pT", parName),
0090 std::format("Pull of {} vs pT", parName),
0091 std::array{ptAxis, pullAxis}));
0092
0093
0094 m_pullVsEtaPhi.emplace(parName,
0095 Acts::Experimental::Histogram3(
0096 std::format("pull_{}_vs_eta_phi", parName),
0097 std::format("Pull of {} vs eta-phi", parName),
0098 std::array{etaAxis, phiAxis, pullAxis}));
0099
0100
0101 m_pullVsEtaPt.emplace(parName,
0102 Acts::Experimental::Histogram3(
0103 std::format("pull_{}_vs_eta_pT", parName),
0104 std::format("Pull of {} vs eta-pT", parName),
0105 std::array{etaAxis, ptAxis, pullAxis}));
0106 }
0107 }
0108
0109 void ResPlotTool::fill(const Acts::GeometryContext& gctx,
0110 const SimParticleState& truthParticle,
0111 const Acts::BoundTrackParameters& fittedParamters) {
0112 using Acts::VectorHelpers::eta;
0113 using Acts::VectorHelpers::perp;
0114 using Acts::VectorHelpers::phi;
0115 using Acts::VectorHelpers::theta;
0116
0117 using enum Acts::BoundIndices;
0118
0119
0120 const Acts::BoundVector& trackParameters = fittedParamters.parameters();
0121 const Acts::BoundMatrix& trackCovariance =
0122 fittedParamters.covariance().value_or(Acts::BoundMatrix::Zero());
0123
0124
0125 const Acts::Surface& pSurface = fittedParamters.referenceSurface();
0126
0127
0128 Acts::BoundVector truthParameters = Acts::BoundVector::Zero();
0129 const Acts::Intersection3D intersection =
0130 pSurface
0131 .intersect(gctx, truthParticle.position(), truthParticle.direction())
0132 .closest();
0133 if (intersection.isValid()) {
0134 const Acts::Result<Acts::Vector2> lpResult = pSurface.globalToLocal(
0135 gctx, intersection.position(), truthParticle.direction());
0136 assert(lpResult.ok());
0137
0138 truthParameters[eBoundLoc0] = lpResult.value()[eBoundLoc0];
0139 truthParameters[eBoundLoc1] = lpResult.value()[eBoundLoc1];
0140 } else {
0141 ACTS_ERROR("Cannot get the truth perigee parameter");
0142 }
0143 truthParameters[eBoundPhi] = phi(truthParticle.direction());
0144 truthParameters[eBoundTheta] = theta(truthParticle.direction());
0145 truthParameters[eBoundQOverP] = truthParticle.qOverP();
0146 truthParameters[eBoundTime] = truthParticle.time();
0147
0148
0149 const double truthEta = eta(truthParticle.direction());
0150 const double truthPhi = phi(truthParticle.direction());
0151 const double truthPt = truthParticle.transverseMomentum();
0152
0153
0154 for (unsigned int paramId = 0; paramId < Acts::eBoundSize; paramId++) {
0155 const std::string& parName = m_cfg.paramNames.at(paramId);
0156
0157 const double residual = trackParameters[paramId] - truthParameters[paramId];
0158 fillResidual(parName, residual, truthEta, truthPhi, truthPt);
0159
0160 const double var = trackCovariance(paramId, paramId);
0161
0162 const double pull = var > 0 ? residual / std::sqrt(var) : nan;
0163 fillPull(parName, pull, truthEta, truthPhi, truthPt);
0164 }
0165
0166
0167 {
0168 const double truthQoverPt = truthParticle.charge() / truthPt;
0169 const double truthPtOverQ = truthPt / truthParticle.charge();
0170 const double recoQoverPt =
0171 trackParameters[eBoundQOverP] / std::sin(trackParameters[eBoundTheta]);
0172 const double residualQoverPt = recoQoverPt - truthQoverPt;
0173 fillResidual(m_cfg.qOverPtName, residualQoverPt, truthEta, truthPhi,
0174 truthPt);
0175
0176 const double residualRelQoverPt = truthPtOverQ * residualQoverPt;
0177 fillResidual(m_cfg.relQoverPtName, residualRelQoverPt, truthEta, truthPhi,
0178 truthPt);
0179
0180 const double covarianceQoverPt = [&]() -> double {
0181 const Acts::Vector2 jacobian{
0182 -recoQoverPt / std::tan(trackParameters[eBoundTheta]),
0183 1 / std::sin(trackParameters[eBoundTheta])};
0184 const Acts::SquareMatrix2 covariance = trackCovariance(
0185 {eBoundTheta, eBoundQOverP}, {eBoundTheta, eBoundQOverP});
0186 return jacobian.transpose() * covariance * jacobian;
0187 }();
0188 const double covarianceRelQoverPt =
0189 Acts::square(truthPtOverQ) * covarianceQoverPt;
0190
0191 const double pullQoverPt =
0192 covarianceQoverPt > 0 ? residualQoverPt / std::sqrt(covarianceQoverPt)
0193 : nan;
0194 fillPull(m_cfg.qOverPtName, pullQoverPt, truthEta, truthPhi, truthPt);
0195
0196 const double pullRelQoverPt =
0197 covarianceRelQoverPt > 0
0198 ? residualRelQoverPt / std::sqrt(covarianceRelQoverPt)
0199 : nan;
0200 fillPull(m_cfg.relQoverPtName, pullRelQoverPt, truthEta, truthPhi, truthPt);
0201 }
0202 }
0203
0204 void ResPlotTool::fillResidual(const std::string& paramName, double residual,
0205 double truthEta, double truthPhi,
0206 double truthPt) {
0207 m_res.at(paramName).fill({residual});
0208 m_resVsEta.at(paramName).fill({truthEta, residual});
0209 m_resVsPt.at(paramName).fill({truthPt, residual});
0210 m_resVsEtaPhi.at(paramName).fill({truthEta, truthPhi, residual});
0211 m_resVsEtaPt.at(paramName).fill({truthEta, truthPt, residual});
0212 }
0213
0214 void ResPlotTool::fillPull(const std::string& paramName, double pull,
0215 double truthEta, double truthPhi, double truthPt) {
0216 m_pull.at(paramName).fill({pull});
0217 m_pullVsEta.at(paramName).fill({truthEta, pull});
0218 m_pullVsPt.at(paramName).fill({truthPt, pull});
0219 m_pullVsEtaPhi.at(paramName).fill({truthEta, truthPhi, pull});
0220 m_pullVsEtaPt.at(paramName).fill({truthEta, truthPt, pull});
0221 }
0222
0223 }