ATLAS Offline Software
AnalogueClusteringToolImpl.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 #ifndef ANALOGUECLUSTERINGTOOLIMPL_ICC
6 #define ANALOGUECLUSTERINGTOOLIMPL_ICC
7 
8 #include <limits>
9 
10 #include <PixelReadoutGeometry/PixelModuleDesign.h>
11 #include "InDetReadoutGeometry/SiDetectorElement.h"
12 #include "InDetMeasurementUtilities/Helpers.h"
13 
14 #include <stdexcept>
15 
16 namespace ActsTrk::detail {
17 
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)
24 {}
25 
26 template <typename calib_data_t, typename traj_t>
27 StatusCode AnalogueClusteringToolImpl<calib_data_t, traj_t>::initialize()
28 {
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");
35 
36 
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 );
43 
44 
45  return StatusCode::SUCCESS;
46 }
47 
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
51 {
52  const InDetDD::SiDetectorElement* element=nullptr;
53  SG::ReadCondHandle<InDetDD::SiDetectorElementCollection> pixelDetEleHandle(
54  m_pixelDetEleCollKey,
55  Gaudi::Hive::currentContext());
56 
57  const InDetDD::SiDetectorElementCollection* detElements(*pixelDetEleHandle);
58 
59  if (!pixelDetEleHandle.isValid() or detElements == nullptr) {
60  ATH_MSG_ERROR(m_pixelDetEleCollKey.fullKey() << " is not available.");
61  }
62  else {
63  element = detElements->getDetectorElement(id);
64  }
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");
68  }
69 
70  return *element;
71 }
72 
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
77 {
78 
79  float projPhi = direction.dot(element.phiAxis());
80  float projEta = direction.dot(element.etaAxis());
81  float projNorm = direction.dot(element.normal());
82 
83  float anglePhi = std::atan2(projPhi, projNorm);
84  float angleEta = std::atan2(projEta, projNorm);
85 
86  // Map the angles of inward-going tracks onto [-PI/2, PI/2]
87  if (anglePhi > M_PI *0.5) {
88  anglePhi -= M_PI;
89  }
90  if (anglePhi < -M_PI *0.5) {
91  anglePhi += M_PI;
92  }
93 
94  // settle the sign/pi periodicity issues
95  float thetaloc;
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;
102  }
103  angleEta = -1 * log(tan(thetaloc * 0.5));
104 
105 
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);
109 
110  return std::make_pair(anglePhi, angleEta);
111 }
112 
113 
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
119 {
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),
128  localPos(1, 0));
129  }
130 
131  // if weighted position, we need to compute the centroid
132  const std::vector<Identifier>& rdos = cluster.rdoList();
133 
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);
142 
143  int col = m_pixelid->eta_index(rid);
144  colmin = std::min(col, colmin);
145  colmax = std::max(col, colmax);
146  }
147 
148  const InDetDD::PixelModuleDesign& design =
149  dynamic_cast<const InDetDD::PixelModuleDesign&>(element.design());
150 
151  InDetDD::SiLocalPosition pos1 =
152  design.positionFromColumnRow(colmin, rowmin);
153 
154  InDetDD::SiLocalPosition pos2 =
155  design.positionFromColumnRow(colmax, rowmin);
156 
157  InDetDD::SiLocalPosition pos3 =
158  design.positionFromColumnRow(colmin, rowmax);
159 
160  InDetDD::SiLocalPosition pos4 =
161  design.positionFromColumnRow(colmax, rowmax);
162 
163  InDetDD::SiLocalPosition centroid = 0.25 * (pos1 + pos2 + pos3 + pos4);
164 
165  double shift = m_lorentzAngleTool->getLorentzShift(element.identifyHash(), Gaudi::Hive::currentContext());
166 
167  return std::make_pair(centroid.xPhi() + shift, centroid.xEta());
168 }
169 
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
173 {
174  SG::ReadCondHandle<calib_data_t> handle(
175  m_clusterErrorKey,
176  Gaudi::Hive::currentContext());
177 
178  if (!handle.isValid()) {
179  ATH_MSG_ERROR(m_clusterErrorKey << " is not available.");
180  return nullptr;
181  }
182 
183  const error_data_t* data = handle->getClusterErrorData();
184  if (data == nullptr) {
185  ATH_MSG_ERROR("No cluster error data corresponding to " << m_clusterErrorKey);
186  return nullptr;
187  }
188 
189  return data;
190 }
191 
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
199 {
200  auto& [anglePhi, angleEta] = angles;
201 
202  std::optional<float> posX {std::nullopt};
203  std::optional<float> posY {std::nullopt};
204 
205  int nrows = cluster.channelsInPhi();
206  int ncols = cluster.channelsInEta();
207 
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,
211  *m_pixelid);
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();
216 
217  std::pair<double, double> delta =
218  errorData.getDelta(idHash,
219  nrows,
220  anglePhi,
221  ncols,
222  angleEta);
223 
224  if (nrows > 1)
225  posX = centroid.first + delta.first * (omX - 0.5);
226 
227  if (ncols > 1)
228  posY = centroid.second + delta.second * (omY - 0.5);
229  } // check on omega values
230  } // check on cluster sizes
231 
232  return std::make_pair(posX, posY);
233 }
234 
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
242  {
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);
247  }
248 
249  Cov cov = cluster.template localCovariance<2>();
250  int nrows = cluster.channelsInPhi();
251  int ncols = cluster.channelsInEta();
252 
253  auto& [anglePhi, angleEta] = angles;
254 
255  if (m_errorStrategy == 0) {
256 
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);
265  } else {
266  errY = *errY / std::sqrt(12);
267  }
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)
272  errX = vX;
273  if (ncols > 1 and vY > 0)
274  errY = vY;
275  }
276  } else if (m_errorStrategy == 1) { // this should check if we are using broad errors in the clustering tool
277 
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;
281 
282  } else { // throw exception for the moment
283  throw std::runtime_error("Only errorStrategy 0 or 1 is supported for the moment");
284  }
285 
286  return std::make_pair(errX, errY);
287 }
288 
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
296 {
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));
302 }
303 
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
311 {
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));
316 }
317 
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
327 {
328  Pos pos = cluster.template localPosition<2>();
329  Cov cov = cluster.template localCovariance<2>();
330 
331  assert(!cluster.rdoList().empty());
332 
333  const error_data_t *errorData = getErrorData();
334  if (errorData == nullptr) {
335  throw std::runtime_error("PixelClusterErrorData is NULL");
336  }
337 
338 
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;
344 
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);
349  }
350  if (errY.has_value()) {
351  double newErr = *errY;
352  cov(1, 1) = std::max(newErr * newErr, cov(1, 1) * m_calibratedCovarianceLowerBound);
353  }
354 
355  return std::make_pair(pos, cov);
356 }
357 
358 template <typename calib_data_t, typename traj_t>
359 void AnalogueClusteringToolImpl<calib_data_t, traj_t>::connect(OnTrackCalibrator<traj_t>& calibrator) const
360 {
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;
368 
369  calibrator.pixelCalibrator. template connect<static_cast<CalibFuncPtr_t>(&AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate)>(this);
370 }
371 
372 template <typename calib_data_t, typename traj_t>
373 void AnalogueClusteringToolImpl<calib_data_t, traj_t>::connectPixelCalibrator(IOnBoundStateCalibratorTool::PixelCalibrator& pixelCalibrator) const
374 {
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;
382 
383  pixelCalibrator.template connect<static_cast<CalibFuncPtr_t>(&AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate)>(this);
384 }
385 
386 template <typename calib_data_t, typename traj_t>
387 bool AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrateAfterMeasurementSelection() const
388 { return m_postCalibration.value(); }
389 
390 } // namespace ActsTrk
391 
392 #endif