2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
5 #ifndef ANALOGUECLUSTERINGTOOLIMPL_ICC
6 #define ANALOGUECLUSTERINGTOOLIMPL_ICC
10 #include <PixelReadoutGeometry/PixelModuleDesign.h>
11 #include "InDetReadoutGeometry/SiDetectorElement.h"
12 #include "InDetMeasurementUtilities/Helpers.h"
16 namespace ActsTrk::detail {
18 template <typename calib_data_t, typename traj_t>
19 AnalogueClusteringToolImpl<calib_data_t, traj_t>::AnalogueClusteringToolImpl(
20 const std::string& type,
21 const std::string& name,
22 const IInterface* parent)
23 : base_class(type, name, parent)
26 template <typename calib_data_t, typename traj_t>
27 StatusCode AnalogueClusteringToolImpl<calib_data_t, traj_t>::initialize()
29 ATH_MSG_DEBUG("Initializing " << AthAlgTool::name() << " ...");
30 ATH_CHECK(m_pixelDetEleCollKey.initialize());
31 ATH_CHECK(m_clusterErrorKey.initialize());
32 ATH_CHECK(m_lorentzAngleTool.retrieve());
33 ATH_CHECK(AthAlgTool::detStore()->retrieve(m_pixelid, "PixelID"));
34 ATH_MSG_DEBUG(AthAlgTool::name() << " successfully initialized");
37 ATH_MSG_DEBUG( m_thickness );
38 ATH_MSG_DEBUG( m_postCalibration );
39 ATH_MSG_DEBUG( m_useWeightedPos );
40 ATH_MSG_DEBUG( m_correctCovariance );
41 ATH_MSG_DEBUG( m_calibratedCovarianceLowerBound );
42 ATH_MSG_DEBUG( m_errorStrategy );
45 return StatusCode::SUCCESS;
48 template <typename calib_data_t, typename traj_t>
49 const InDetDD::SiDetectorElement&
50 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getDetectorElement(xAOD::DetectorIDHashType id) const
52 const InDetDD::SiDetectorElement* element=nullptr;
53 SG::ReadCondHandle<InDetDD::SiDetectorElementCollection> pixelDetEleHandle(
55 Gaudi::Hive::currentContext());
57 const InDetDD::SiDetectorElementCollection* detElements(*pixelDetEleHandle);
59 if (!pixelDetEleHandle.isValid() or detElements == nullptr) {
60 ATH_MSG_ERROR(m_pixelDetEleCollKey.fullKey() << " is not available.");
63 element = detElements->getDetectorElement(id);
65 if (element == nullptr) {
66 ATH_MSG_ERROR("No element corresponding to hash " << id << " for " << m_pixelDetEleCollKey.fullKey());
67 throw std::runtime_error("SiDetectorElement is NULL");
73 template <typename calib_data_t, typename traj_t>
74 std::pair<float, float>
75 AnalogueClusteringToolImpl<calib_data_t, traj_t>::anglesOfIncidence(const InDetDD::SiDetectorElement& element,
76 const Acts::Vector3& direction) const
79 float projPhi = direction.dot(element.phiAxis());
80 float projEta = direction.dot(element.etaAxis());
81 float projNorm = direction.dot(element.normal());
83 float anglePhi = std::atan2(projPhi, projNorm);
84 float angleEta = std::atan2(projEta, projNorm);
86 // Map the angles of inward-going tracks onto [-PI/2, PI/2]
87 if (anglePhi > M_PI *0.5) {
90 if (anglePhi < -M_PI *0.5) {
94 // settle the sign/pi periodicity issues
96 if (angleEta > -0.5 * M_PI && angleEta < M_PI / 2.) {
97 thetaloc = M_PI_2 - angleEta;
98 } else if (angleEta > M_PI_2 && angleEta < M_PI) {
99 thetaloc = 1.5 * M_PI - angleEta;
100 } else { // 3rd quadrant
101 thetaloc = -M_PI_2 - angleEta;
103 angleEta = -1 * log(tan(thetaloc * 0.5));
106 // Subtract the Lorentz angle effect
107 float angleShift = m_lorentzAngleTool->getTanLorentzAngle(element.identifyHash(), Gaudi::Hive::currentContext());
108 anglePhi = std::atan(std::tan(anglePhi) - element.design().readoutSide() * angleShift);
110 return std::make_pair(anglePhi, angleEta);
114 template <typename calib_data_t, typename traj_t>
115 std::pair<float, float>
116 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getCentroid(
117 const xAOD::PixelCluster& cluster,
118 const InDetDD::SiDetectorElement& element) const
120 // Different behaviours if pixel uncalibrated cluster use digital or
121 // charge-weighted local position
122 if (not m_useWeightedPos) {
123 // We return the cluster local position here
124 // computed from the clustering tool
125 // Eventually we will have a NN for the on-track calibration instead this instead
126 const auto& localPos = cluster.template localPosition<2>();
127 return std::make_pair(localPos(0, 0),
131 // if weighted position, we need to compute the centroid
132 const std::vector<Identifier>& rdos = cluster.rdoList();
134 int rowmin = std::numeric_limits<int>::max();
135 int rowmax = std::numeric_limits<int>::min();
136 int colmin = std::numeric_limits<int>::max();
137 int colmax = std::numeric_limits<int>::min();
138 for (const Identifier& rid : rdos) {
139 int row = m_pixelid->phi_index(rid);
140 rowmin = std::min(row, rowmin);
141 rowmax = std::max(row, rowmax);
143 int col = m_pixelid->eta_index(rid);
144 colmin = std::min(col, colmin);
145 colmax = std::max(col, colmax);
148 const InDetDD::PixelModuleDesign& design =
149 dynamic_cast<const InDetDD::PixelModuleDesign&>(element.design());
151 InDetDD::SiLocalPosition pos1 =
152 design.positionFromColumnRow(colmin, rowmin);
154 InDetDD::SiLocalPosition pos2 =
155 design.positionFromColumnRow(colmax, rowmin);
157 InDetDD::SiLocalPosition pos3 =
158 design.positionFromColumnRow(colmin, rowmax);
160 InDetDD::SiLocalPosition pos4 =
161 design.positionFromColumnRow(colmax, rowmax);
163 InDetDD::SiLocalPosition centroid = 0.25 * (pos1 + pos2 + pos3 + pos4);
165 double shift = m_lorentzAngleTool->getLorentzShift(element.identifyHash(), Gaudi::Hive::currentContext());
167 return std::make_pair(centroid.xPhi() + shift, centroid.xEta());
170 template <typename calib_data_t, typename traj_t>
171 const typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::error_data_t*
172 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getErrorData() const
174 SG::ReadCondHandle<calib_data_t> handle(
176 Gaudi::Hive::currentContext());
178 if (!handle.isValid()) {
179 ATH_MSG_ERROR(m_clusterErrorKey << " is not available.");
183 const error_data_t* data = handle->getClusterErrorData();
184 if (data == nullptr) {
185 ATH_MSG_ERROR("No cluster error data corresponding to " << m_clusterErrorKey);
192 template <typename calib_data_t, typename traj_t>
193 std::pair<std::optional<float>, std::optional<float>>
194 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getCorrectedPosition(
195 const xAOD::PixelCluster& cluster,
196 const error_data_t& errorData,
197 const InDetDD::SiDetectorElement& element,
198 const std::pair<float, float>& angles) const
200 auto& [anglePhi, angleEta] = angles;
202 std::optional<float> posX {std::nullopt};
203 std::optional<float> posY {std::nullopt};
205 int nrows = cluster.channelsInPhi();
206 int ncols = cluster.channelsInEta();
208 // If size of cluster (in any direction) is higher than 1, we may need to apply a correction
209 if (nrows > 1 or ncols > 1) {
210 const auto& [omX, omY] = TrackingUtilities::computeOmegas(cluster,
212 // if the omegas are valid, we can apply the correction
213 if (omX > -0.5 and omY > -0.5) {
214 std::pair<float, float> centroid = getCentroid(cluster, element);
215 IdentifierHash idHash = element.identifyHash();
217 std::pair<double, double> delta =
218 errorData.getDelta(idHash,
225 posX = centroid.first + delta.first * (omX - 0.5);
228 posY = centroid.second + delta.second * (omY - 0.5);
229 } // check on omega values
230 } // check on cluster sizes
232 return std::make_pair(posX, posY);
235 template <typename calib_data_t, typename traj_t>
236 std::pair<std::optional<float>, std::optional<float>>
237 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getCorrectedError(
238 const error_data_t& errorData,
239 const InDetDD::SiDetectorElement& element,
240 const std::pair<float, float>& angles,
241 const xAOD::PixelCluster& cluster) const
243 std::optional<float> errX {std::nullopt};
244 std::optional<float> errY {std::nullopt};
245 if (not m_correctCovariance) {
246 return std::make_pair(errX, errY);
249 Cov cov = cluster.template localCovariance<2>();
250 int nrows = cluster.channelsInPhi();
251 int ncols = cluster.channelsInEta();
253 auto& [anglePhi, angleEta] = angles;
255 if (m_errorStrategy == 0) {
257 // Special case for very shallow tracks
258 // Error estimated from geometrical projection of
259 // the track path in silicon onto the module surface
260 if (std::abs(anglePhi) > 1) {
261 errX = m_thickness * Acts::UnitConstants::um * std::tan(std::abs(anglePhi)) / std::sqrt(12);
262 errY = m_thickness * Acts::UnitConstants::um * std::tan(std::abs(angleEta));
263 if (cluster.widthInEta() > errY) {
264 errY = cluster.widthInEta() / std::sqrt(12);
266 errY = *errY / std::sqrt(12);
268 } else if (nrows > 1 or ncols > 1) {
269 IdentifierHash idHash = element.identifyHash();
270 auto [vX, vY] = errorData.getDeltaError(idHash);
271 if (nrows > 1 and vX > 0)
273 if (ncols > 1 and vY > 0)
276 } else if (m_errorStrategy == 1) { // this should check if we are using broad errors in the clustering tool
278 //This assumes that we ran broad errors in the clustering tool
279 errX = std::sqrt(cov(0,0)) / nrows;
280 errY = std::sqrt(cov(1,1)) / ncols;
282 } else { // throw exception for the moment
283 throw std::runtime_error("Only errorStrategy 0 or 1 is supported for the moment");
286 return std::make_pair(errX, errY);
289 template <typename calib_data_t, typename traj_t>
290 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
291 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
292 AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate(const Acts::GeometryContext& gctx,
293 const Acts::CalibrationContext& cctx,
294 const xAOD::PixelCluster& cluster,
295 const TrackStateProxy& state) const
297 const InDetDD::SiDetectorElement &detElement = getDetectorElement(cluster.identifierHash());
298 Acts::Vector3 direction = Acts::makeDirectionFromPhiTheta(
299 state.parameters()[Acts::eBoundPhi],
300 state.parameters()[Acts::eBoundTheta]);
301 return calibrate(gctx, cctx, cluster, detElement, anglesOfIncidence(detElement, direction));
304 template <typename calib_data_t, typename traj_t>
305 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
306 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
307 AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate(const Acts::GeometryContext& gctx,
308 const Acts::CalibrationContext& cctx,
309 const xAOD::PixelCluster& cluster,
310 const Acts::BoundTrackParameters& bound_parameters) const
312 const InDetDD::SiDetectorElement &detElement = getDetectorElement(cluster.identifierHash());
313 Acts::Vector3 direction = Acts::makeDirectionFromPhiTheta(bound_parameters.parameters()[Acts::eBoundPhi],
314 bound_parameters.parameters()[Acts::eBoundTheta]);
315 return calibrate(gctx, cctx, cluster, detElement, anglesOfIncidence(detElement, direction));
318 template <typename calib_data_t, typename traj_t>
319 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
320 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
321 AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate(
322 const Acts::GeometryContext& /*gctx*/,
323 const Acts::CalibrationContext& /*cctx*/,
324 const xAOD::PixelCluster& cluster,
325 const InDetDD::SiDetectorElement& detElement,
326 const std::pair<float, float>& angles) const
328 Pos pos = cluster.template localPosition<2>();
329 Cov cov = cluster.template localCovariance<2>();
331 assert(!cluster.rdoList().empty());
333 const error_data_t *errorData = getErrorData();
334 if (errorData == nullptr) {
335 throw std::runtime_error("PixelClusterErrorData is NULL");
339 auto [posX, posY] = getCorrectedPosition(cluster, *errorData, detElement, angles);
340 if (posX.has_value())
341 pos[Acts::eBoundLoc0] = *posX;
342 if (posY.has_value())
343 pos[Acts::eBoundLoc1] = *posY;
345 auto [errX, errY] = getCorrectedError(*errorData, detElement, angles, cluster);
346 if (errX.has_value()) {
347 double newErr = *errX;
348 cov(0, 0) = std::max(newErr * newErr, cov(0, 0) * m_calibratedCovarianceLowerBound);
350 if (errY.has_value()) {
351 double newErr = *errY;
352 cov(1, 1) = std::max(newErr * newErr, cov(1, 1) * m_calibratedCovarianceLowerBound);
355 return std::make_pair(pos, cov);
358 template <typename calib_data_t, typename traj_t>
359 void AnalogueClusteringToolImpl<calib_data_t, traj_t>::connect(OnTrackCalibrator<traj_t>& calibrator) const
361 using CalibFuncPtr_t =
362 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
363 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
364 (AnalogueClusteringToolImpl<calib_data_t, traj_t>:: *)(const Acts::GeometryContext&,
365 const Acts::CalibrationContext&,
366 const xAOD::PixelCluster&,
367 const TrackStateProxy&) const;
369 calibrator.pixelCalibrator. template connect<static_cast<CalibFuncPtr_t>(&AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate)>(this);
372 template <typename calib_data_t, typename traj_t>
373 void AnalogueClusteringToolImpl<calib_data_t, traj_t>::connectPixelCalibrator(IOnBoundStateCalibratorTool::PixelCalibrator& pixelCalibrator) const
375 using CalibFuncPtr_t =
376 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
377 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
378 (AnalogueClusteringToolImpl<calib_data_t, traj_t>:: *)(const Acts::GeometryContext&,
379 const Acts::CalibrationContext&,
380 const xAOD::PixelCluster&,
381 const Acts::BoundTrackParameters&) const;
383 pixelCalibrator.template connect<static_cast<CalibFuncPtr_t>(&AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate)>(this);
386 template <typename calib_data_t, typename traj_t>
387 bool AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrateAfterMeasurementSelection() const
388 { return m_postCalibration.value(); }
390 } // namespace ActsTrk