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"
13 namespace ActsTrk::detail {
15 template <typename calib_data_t, typename traj_t>
16 AnalogueClusteringToolImpl<calib_data_t, traj_t>::AnalogueClusteringToolImpl(
17 const std::string& type,
18 const std::string& name,
19 const IInterface* parent)
20 : base_class(type, name, parent)
23 template <typename calib_data_t, typename traj_t>
24 StatusCode AnalogueClusteringToolImpl<calib_data_t, traj_t>::initialize()
26 ATH_MSG_DEBUG("Initializing " << AthAlgTool::name() << " ...");
27 ATH_CHECK(m_pixelDetEleCollKey.initialize());
28 ATH_CHECK(m_clusterErrorKey.initialize());
29 ATH_CHECK(m_lorentzAngleTool.retrieve());
30 ATH_CHECK(AthAlgTool::detStore()->retrieve(m_pixelid, "PixelID"));
31 ATH_MSG_DEBUG(AthAlgTool::name() << " successfully initialized");
33 ATH_MSG_DEBUG( m_thickness );
34 ATH_MSG_DEBUG( m_postCalibration );
35 ATH_MSG_DEBUG( m_correctCovariance );
36 ATH_MSG_DEBUG( m_calibratedCovarianceLowerBound );
38 return StatusCode::SUCCESS;
41 template <typename calib_data_t, typename traj_t>
42 const InDetDD::SiDetectorElement&
43 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getDetectorElement(xAOD::DetectorIDHashType id) const
45 const InDetDD::SiDetectorElement* element=nullptr;
46 SG::ReadCondHandle<InDetDD::SiDetectorElementCollection> pixelDetEleHandle(
48 Gaudi::Hive::currentContext());
50 const InDetDD::SiDetectorElementCollection* detElements(*pixelDetEleHandle);
52 if (!pixelDetEleHandle.isValid() or detElements == nullptr) {
53 ATH_MSG_ERROR(m_pixelDetEleCollKey.fullKey() << " is not available.");
56 element = detElements->getDetectorElement(id);
58 if (element == nullptr) {
59 ATH_MSG_ERROR("No element corresponding to hash " << id << " for " << m_pixelDetEleCollKey.fullKey());
60 throw std::runtime_error("SiDetectorElement is NULL");
66 template <typename calib_data_t, typename traj_t>
67 std::pair<float, float>
68 AnalogueClusteringToolImpl<calib_data_t, traj_t>::anglesOfIncidence(const InDetDD::SiDetectorElement& element,
69 const Acts::Vector3& direction) const
72 float projPhi = direction.dot(element.phiAxis());
73 float projEta = direction.dot(element.etaAxis());
74 float projNorm = direction.dot(element.normal());
76 float anglePhi = std::atan2(projPhi, projNorm);
77 float angleEta = std::atan2(projEta, projNorm);
79 // Map the angles of inward-going tracks onto [-PI/2, PI/2]
80 if (anglePhi > M_PI *0.5) {
83 if (anglePhi < -M_PI *0.5) {
87 // settle the sign/pi periodicity issues
89 if (angleEta > -0.5 * M_PI && angleEta < M_PI / 2.) {
90 thetaloc = M_PI_2 - angleEta;
91 } else if (angleEta > M_PI_2 && angleEta < M_PI) {
92 thetaloc = 1.5 * M_PI - angleEta;
93 } else { // 3rd quadrant
94 thetaloc = -M_PI_2 - angleEta;
96 angleEta = -1 * log(tan(thetaloc * 0.5));
99 // Subtract the Lorentz angle effect
100 float angleShift = m_lorentzAngleTool->getTanLorentzAngle(element.identifyHash(), Gaudi::Hive::currentContext());
101 anglePhi = std::atan(std::tan(anglePhi) - element.design().readoutSide() * angleShift);
103 return std::make_pair(anglePhi, angleEta);
107 template <typename calib_data_t, typename traj_t>
108 std::pair<float, float>
109 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getCentroid(
110 const std::vector<Identifier>& rdos,
111 const InDetDD::SiDetectorElement& element) const
114 int rowmin = std::numeric_limits<int>::max();
115 int rowmax = std::numeric_limits<int>::min();
116 int colmin = std::numeric_limits<int>::max();
117 int colmax = std::numeric_limits<int>::min();
118 for (const Identifier& rid : rdos) {
119 int row = m_pixelid->phi_index(rid);
120 rowmin = std::min(row, rowmin);
121 rowmax = std::max(row, rowmax);
123 int col = m_pixelid->eta_index(rid);
124 colmin = std::min(col, colmin);
125 colmax = std::max(col, colmax);
128 const InDetDD::PixelModuleDesign& design =
129 dynamic_cast<const InDetDD::PixelModuleDesign&>(element.design());
131 InDetDD::SiLocalPosition pos1 =
132 design.positionFromColumnRow(colmin, rowmin);
134 InDetDD::SiLocalPosition pos2 =
135 design.positionFromColumnRow(colmax, rowmin);
137 InDetDD::SiLocalPosition pos3 =
138 design.positionFromColumnRow(colmin, rowmax);
140 InDetDD::SiLocalPosition pos4 =
141 design.positionFromColumnRow(colmax, rowmax);
143 InDetDD::SiLocalPosition centroid = 0.25 * (pos1 + pos2 + pos3 + pos4);
145 double shift = m_lorentzAngleTool->getLorentzShift(element.identifyHash(), Gaudi::Hive::currentContext());
147 return std::make_pair(centroid.xPhi() + shift, centroid.xEta());
150 template <typename calib_data_t, typename traj_t>
151 const typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::error_data_t*
152 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getErrorData() const
154 SG::ReadCondHandle<calib_data_t> handle(
156 Gaudi::Hive::currentContext());
158 if (!handle.isValid()) {
159 ATH_MSG_ERROR(m_clusterErrorKey << " is not available.");
163 const error_data_t* data = handle->getClusterErrorData();
164 if (data == nullptr) {
165 ATH_MSG_ERROR("No cluster error data corresponding to " << m_clusterErrorKey);
172 template <typename calib_data_t, typename traj_t>
173 std::pair<std::optional<float>, std::optional<float>>
174 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getCorrectedPosition(
175 const std::vector<Identifier>& rdos,
176 const error_data_t& errorData,
177 const InDetDD::SiDetectorElement& element,
178 const std::pair<float, float>& angles,
179 const xAOD::PixelCluster& cluster) const
181 // TODO validate these angles
182 auto& [anglePhi, angleEta] = angles;
184 std::optional<float> posX;
185 std::optional<float> posY;
187 float omX = cluster.omegaX();
188 float omY = cluster.omegaY();
189 int nrows = cluster.channelsInPhi();
190 int ncols = cluster.channelsInEta();
192 if (omX > -0.5 && omY > -0.5 && (nrows > 1 || ncols > 1)) {
194 std::pair<float, float> centroid = getCentroid(rdos, element);
196 IdentifierHash idHash = element.identifyHash();
198 std::pair<double, double> delta =
207 posX = centroid.first + delta.first * (omX - 0.5);
210 posY = centroid.second + delta.second * (omY - 0.5);
213 return std::make_pair(posX, posY);
216 template <typename calib_data_t, typename traj_t>
217 std::pair<std::optional<float>, std::optional<float>>
218 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getCorrectedError(
219 const error_data_t& errorData,
220 const InDetDD::SiDetectorElement& element,
221 const std::pair<float, float>& angles,
222 const xAOD::PixelCluster& cluster) const
224 std::optional<float> errX {std::nullopt};
225 std::optional<float> errY {std::nullopt};
226 if (not m_correctCovariance) {
227 return std::make_pair(errX, errY);
230 int nrows = cluster.channelsInPhi();
231 int ncols = cluster.channelsInEta();
233 auto& [anglePhi, angleEta] = angles;
235 // Special case for very shallow tracks
236 // Error estimated from geometrical projection of
237 // the track path in silicon onto the module surface
238 if (std::abs(anglePhi) > 1) {
239 errX = m_thickness * Acts::UnitConstants::um * std::tan(std::abs(anglePhi)) / std::sqrt(12);
240 errY = m_thickness * Acts::UnitConstants::um * std::tan(std::abs(angleEta));
241 if (cluster.widthInEta() > errY) {
242 errY = cluster.widthInEta() / std::sqrt(12);
244 errY = *errY / std::sqrt(12);
246 } else if (nrows > 1 or ncols > 1) {
247 IdentifierHash idHash = element.identifyHash();
248 auto [vX, vY] = errorData.getDeltaError(idHash);
255 return std::make_pair(errX, errY);
258 template <typename calib_data_t, typename traj_t>
259 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
260 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
261 AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate(
262 const Acts::GeometryContext& gctx,
263 const Acts::CalibrationContext& cctx,
264 const xAOD::PixelCluster& cluster,
265 const TrackStateProxy& state) const
267 const InDetDD::SiDetectorElement &detElement = getDetectorElement(cluster.identifierHash());
268 Acts::Vector3 direction = Acts::makeDirectionFromPhiTheta(
269 state.parameters()[Acts::eBoundPhi],
270 state.parameters()[Acts::eBoundTheta]);
271 return calibrate(gctx, cctx, cluster, detElement, anglesOfIncidence(detElement, direction));
274 template <typename calib_data_t, typename traj_t>
275 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
276 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
277 AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate(
278 const Acts::GeometryContext& gctx,
279 const Acts::CalibrationContext& cctx,
280 const xAOD::PixelCluster& cluster,
281 const Acts::BoundTrackParameters& bound_parameters) const
283 const InDetDD::SiDetectorElement &detElement = getDetectorElement(cluster.identifierHash());
284 Acts::Vector3 direction = Acts::makeDirectionFromPhiTheta(
285 bound_parameters.parameters()[Acts::eBoundPhi],
286 bound_parameters.parameters()[Acts::eBoundTheta]);
287 return calibrate(gctx, cctx, cluster, detElement, anglesOfIncidence(detElement, direction));
290 template <typename calib_data_t, typename traj_t>
291 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
292 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
293 AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate(
294 const Acts::GeometryContext& /*gctx*/,
295 const Acts::CalibrationContext& /*cctx*/,
296 const xAOD::PixelCluster& cluster,
297 const InDetDD::SiDetectorElement& detElement,
298 const std::pair<float, float>& angles) const
300 Pos pos = cluster.template localPosition<2>();
301 Cov cov = cluster.template localCovariance<2>();
303 assert(!cluster.rdoList().empty());
305 const error_data_t *errorData = getErrorData();
306 if (errorData == nullptr) {
307 throw std::runtime_error("PixelClusterErrorData is NULL");
311 auto [posX, posY] = getCorrectedPosition(cluster.rdoList(), *errorData, detElement, angles, cluster);
312 if (posX.has_value())
313 pos[Acts::eBoundLoc0] = *posX;
314 if (posY.has_value())
315 pos[Acts::eBoundLoc1] = *posY;
317 auto [errX, errY] = getCorrectedError(*errorData, detElement, angles, cluster);
318 if (errX.has_value()) {
319 double newErr = *errX;
320 cov(0, 0) = std::max(newErr * newErr, cov(0, 0) * m_calibratedCovarianceLowerBound);
322 if (errY.has_value()) {
323 double newErr = *errY;
324 cov(1, 1) = std::max(newErr * newErr, cov(1, 1) * m_calibratedCovarianceLowerBound);
327 return std::make_pair(pos, cov);
330 template <typename calib_data_t, typename traj_t>
331 void AnalogueClusteringToolImpl<calib_data_t, traj_t>::connect(OnTrackCalibrator<traj_t>& calibrator) const
333 using CalibFuncPtr_t =
334 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
335 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
336 (AnalogueClusteringToolImpl<calib_data_t, traj_t>:: *)(const Acts::GeometryContext&,
337 const Acts::CalibrationContext&,
338 const xAOD::PixelCluster&,
339 const TrackStateProxy&) const;
341 calibrator.pixelCalibrator. template connect<static_cast<CalibFuncPtr_t>(&AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate)>(this);
344 template <typename calib_data_t, typename traj_t>
345 void AnalogueClusteringToolImpl<calib_data_t, traj_t>::connectPixelCalibrator(IOnBoundStateCalibratorTool::PixelCalibrator& pixelCalibrator) const
347 using CalibFuncPtr_t =
348 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
349 typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
350 (AnalogueClusteringToolImpl<calib_data_t, traj_t>:: *)(const Acts::GeometryContext&,
351 const Acts::CalibrationContext&,
352 const xAOD::PixelCluster&,
353 const Acts::BoundTrackParameters&) const;
355 pixelCalibrator.template connect<static_cast<CalibFuncPtr_t>(&AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate)>(this);
358 template <typename calib_data_t, typename traj_t>
359 bool AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrateAfterMeasurementSelection() const
360 { return m_postCalibration.value(); }
362 } // namespace ActsTrk