ATLAS Offline Software
AnalogueClusteringToolImpl.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2024 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 
13 namespace ActsTrk::detail {
14 
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)
21 {}
22 
23 template <typename calib_data_t, typename traj_t>
24 StatusCode AnalogueClusteringToolImpl<calib_data_t, traj_t>::initialize()
25 {
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");
32  return StatusCode::SUCCESS;
33 }
34 
35 template <typename calib_data_t, typename traj_t>
36 const InDetDD::SiDetectorElement&
37 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getDetectorElement(xAOD::DetectorIDHashType id) const
38 {
39  const InDetDD::SiDetectorElement* element=nullptr;
40  SG::ReadCondHandle<InDetDD::SiDetectorElementCollection> pixelDetEleHandle(
41  m_pixelDetEleCollKey,
42  Gaudi::Hive::currentContext());
43 
44  const InDetDD::SiDetectorElementCollection* detElements(*pixelDetEleHandle);
45 
46  if (!pixelDetEleHandle.isValid() or detElements == nullptr) {
47  ATH_MSG_ERROR(m_pixelDetEleCollKey.fullKey() << " is not available.");
48  }
49  else {
50  element = detElements->getDetectorElement(id);
51  }
52  if (element == nullptr) {
53  ATH_MSG_ERROR("No element corresponding to hash " << id << " for " << m_pixelDetEleCollKey.fullKey());
54  throw std::runtime_error("SiDetectorElement is NULL");
55  }
56 
57  return *element;
58 }
59 
60 template <typename calib_data_t, typename traj_t>
61 std::pair<float, float>
62 AnalogueClusteringToolImpl<calib_data_t, traj_t>::anglesOfIncidence(const InDetDD::SiDetectorElement& element,
63  const Acts::Vector3& direction) const
64 {
65 
66  float projPhi = direction.dot(element.phiAxis());
67  float projEta = direction.dot(element.etaAxis());
68  float projNorm = direction.dot(element.normal());
69 
70  float anglePhi = std::atan2(projPhi, projNorm);
71  float angleEta = std::atan2(projEta, projNorm);
72 
73  // Map the angles of inward-going tracks onto [-PI/2, PI/2]
74  if (anglePhi > M_PI *0.5) {
75  anglePhi -= M_PI;
76  }
77  if (anglePhi < -M_PI *0.5) {
78  anglePhi += M_PI;
79  }
80 
81  // settle the sign/pi periodicity issues
82  float thetaloc;
83  if (angleEta > -0.5 * M_PI && angleEta < M_PI / 2.) {
84  thetaloc = M_PI_2 - angleEta;
85  } else if (angleEta > M_PI_2 && angleEta < M_PI) {
86  thetaloc = 1.5 * M_PI - angleEta;
87  } else { // 3rd quadrant
88  thetaloc = -M_PI_2 - angleEta;
89  }
90  angleEta = -1 * log(tan(thetaloc * 0.5));
91 
92 
93  // Subtract the Lorentz angle effect
94  float angleShift = m_lorentzAngleTool->getTanLorentzAngle(element.identifyHash(), Gaudi::Hive::currentContext());
95  anglePhi = std::atan(std::tan(anglePhi) - element.design().readoutSide() * angleShift);
96 
97  return std::make_pair(anglePhi, angleEta);
98 }
99 
100 
101 template <typename calib_data_t, typename traj_t>
102 std::pair<float, float>
103 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getCentroid(
104  const std::vector<Identifier>& rdos,
105  const InDetDD::SiDetectorElement& element) const
106 {
107 
108  int rowmin = std::numeric_limits<int>::max();
109  int rowmax = std::numeric_limits<int>::min();
110  int colmin = std::numeric_limits<int>::max();
111  int colmax = std::numeric_limits<int>::min();
112  for (const Identifier& rid : rdos) {
113  int row = m_pixelid->phi_index(rid);
114  rowmin = std::min(row, rowmin);
115  rowmax = std::max(row, rowmax);
116 
117  int col = m_pixelid->eta_index(rid);
118  colmin = std::min(col, colmin);
119  colmax = std::max(col, colmax);
120  }
121 
122  const InDetDD::PixelModuleDesign& design =
123  dynamic_cast<const InDetDD::PixelModuleDesign&>(element.design());
124 
125  InDetDD::SiLocalPosition pos1 =
126  design.positionFromColumnRow(colmin, rowmin);
127 
128  InDetDD::SiLocalPosition pos2 =
129  design.positionFromColumnRow(colmax, rowmin);
130 
131  InDetDD::SiLocalPosition pos3 =
132  design.positionFromColumnRow(colmin, rowmax);
133 
134  InDetDD::SiLocalPosition pos4 =
135  design.positionFromColumnRow(colmax, rowmax);
136 
137  InDetDD::SiLocalPosition centroid = 0.25 * (pos1 + pos2 + pos3 + pos4);
138 
139  double shift = m_lorentzAngleTool->getLorentzShift(element.identifyHash(), Gaudi::Hive::currentContext());
140 
141  return std::make_pair(centroid.xPhi() + shift, centroid.xEta());
142 }
143 
144 template <typename calib_data_t, typename traj_t>
145 const typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::error_data_t*
146 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getErrorData() const
147 {
148  SG::ReadCondHandle<calib_data_t> handle(
149  m_clusterErrorKey,
150  Gaudi::Hive::currentContext());
151 
152  if (!handle.isValid()) {
153  ATH_MSG_ERROR(m_clusterErrorKey << " is not available.");
154  return nullptr;
155  }
156 
157  const error_data_t* data = handle->getClusterErrorData();
158  if (data == nullptr) {
159  ATH_MSG_ERROR("No cluster error data corresponding to " << m_clusterErrorKey);
160  return nullptr;
161  }
162 
163  return data;
164 }
165 
166 template <typename calib_data_t, typename traj_t>
167 std::pair<std::optional<float>, std::optional<float>>
168 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getCorrectedPosition(
169  const std::vector<Identifier>& rdos,
170  const error_data_t& errorData,
171  const InDetDD::SiDetectorElement& element,
172  const std::pair<float, float>& angles,
173  const xAOD::PixelCluster& cluster) const
174 {
175  // TODO validate these angles
176  auto& [anglePhi, angleEta] = angles;
177 
178  std::optional<float> posX;
179  std::optional<float> posY;
180 
181  float omX = cluster.omegaX();
182  float omY = cluster.omegaY();
183  int nrows = cluster.channelsInPhi();
184  int ncols = cluster.channelsInEta();
185 
186  if (omX > -0.5 && omY > -0.5 && (nrows > 1 || ncols > 1)) {
187 
188  std::pair<float, float> centroid = getCentroid(rdos, element);
189 
190  Identifier id = element.identify();
191  std::pair<double, double> delta =
192  errorData.getDelta(
193  &id,
194  nrows,
195  anglePhi,
196  ncols,
197  angleEta);
198 
199  if (nrows > 1)
200  posX = centroid.first + delta.first * (omX - 0.5);
201 
202  if (ncols > 1)
203  posY = centroid.second + delta.second * (omY - 0.5);
204  }
205 
206  return std::make_pair(posX, posY);
207 }
208 
209 template <typename calib_data_t, typename traj_t>
210 std::pair<std::optional<float>, std::optional<float>>
211 AnalogueClusteringToolImpl<calib_data_t, traj_t>::getCorrectedError(
212  const error_data_t& errorData,
213  const InDetDD::SiDetectorElement& element,
214  const std::pair<float, float>& angles,
215  const xAOD::PixelCluster& cluster) const
216 {
217  std::optional<float> errX;
218  std::optional<float> errY;
219 
220  int nrows = cluster.channelsInPhi();
221  int ncols = cluster.channelsInEta();
222 
223  auto& [anglePhi, angleEta] = angles;
224 
225  // Special case for very shallow tracks
226  // Error estimated from geometrical projection of
227  // the track path in silicon onto the module surface
228  if (std::abs(anglePhi) > 1) {
229  errX = m_thickness * Acts::UnitConstants::um * std::tan(std::abs(anglePhi)) / std::sqrt(12);
230  errY = m_thickness * Acts::UnitConstants::um * std::tan(std::abs(angleEta));
231  if (cluster.widthInEta() > errY) {
232  errY = cluster.widthInEta() / std::sqrt(12);
233  } else {
234  errY = *errY / std::sqrt(12);
235  }
236  } else if (nrows > 1 && ncols > 1) {
237  Identifier id = element.identify();
238  auto [vX, vY] = errorData.getDeltaError(&id);
239  if (vX > 0)
240  errX = vX;
241  if (vY > 0)
242  errY = vY;
243  }
244 
245  return std::make_pair(errX, errY);
246 }
247 
248 template <typename calib_data_t, typename traj_t>
249 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
250  typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
251 AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate(
252  const Acts::GeometryContext& gctx,
253  const Acts::CalibrationContext& cctx,
254  const xAOD::PixelCluster& cluster,
255  const TrackStateProxy& state) const
256 {
257  const InDetDD::SiDetectorElement &detElement = getDetectorElement(cluster.identifierHash());
258  Acts::Vector3 direction = Acts::makeDirectionFromPhiTheta(
259  state.parameters()[Acts::eBoundPhi],
260  state.parameters()[Acts::eBoundTheta]);
261  return calibrate(gctx, cctx, cluster, detElement, anglesOfIncidence(detElement, direction));
262 }
263 
264 template <typename calib_data_t, typename traj_t>
265 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
266  typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
267 AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate(
268  const Acts::GeometryContext& gctx,
269  const Acts::CalibrationContext& cctx,
270  const xAOD::PixelCluster& cluster,
271  const Acts::BoundTrackParameters& bound_parameters) const
272 {
273  const InDetDD::SiDetectorElement &detElement = getDetectorElement(cluster.identifierHash());
274  Acts::Vector3 direction = Acts::makeDirectionFromPhiTheta(
275  bound_parameters.parameters()[Acts::eBoundPhi],
276  bound_parameters.parameters()[Acts::eBoundTheta]);
277  return calibrate(gctx, cctx, cluster, detElement, anglesOfIncidence(detElement, direction));
278 }
279 
280 template <typename calib_data_t, typename traj_t>
281 std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
282  typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
283 AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate(
284  const Acts::GeometryContext& /*gctx*/,
285  const Acts::CalibrationContext& /*cctx*/,
286  const xAOD::PixelCluster& cluster,
287  const InDetDD::SiDetectorElement& detElement,
288  const std::pair<float, float>& angles) const
289 {
290  Pos pos = cluster.template localPosition<2>();
291  Cov cov = cluster.template localCovariance<2>();
292 
293  assert(!cluster.rdoList().empty());
294 
295  const error_data_t *errorData = getErrorData();
296  if (errorData == nullptr) {
297  throw std::runtime_error("PixelClusterErrorData is NULL");
298  }
299 
300 
301  auto [posX, posY] = getCorrectedPosition(cluster.rdoList(), *errorData, detElement, angles, cluster);
302  if (posX.has_value())
303  pos[Acts::eBoundLoc0] = *posX;
304  if (posY.has_value())
305  pos[Acts::eBoundLoc1] = *posY;
306 
307  auto [errX, errY] = getCorrectedError(*errorData, detElement, angles, cluster);
308  if (errX.has_value())
309  cov(0, 0) = (*errX) * (*errX);
310  if (errY.has_value())
311  cov(1, 1) = (*errY) * (*errY);
312 
313  return std::make_pair(pos, cov);
314 }
315 
316 template <typename calib_data_t, typename traj_t>
317 void AnalogueClusteringToolImpl<calib_data_t, traj_t>::connect(OnTrackCalibrator<traj_t>& calibrator) const
318 {
319  using CalibFuncPtr_t =
320  std::pair<typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Pos,
321  typename AnalogueClusteringToolImpl<calib_data_t, traj_t>::Cov>
322  (AnalogueClusteringToolImpl<calib_data_t, traj_t>:: *)(const Acts::GeometryContext&,
323  const Acts::CalibrationContext&,
324  const xAOD::PixelCluster&,
325  const TrackStateProxy&) const;
326 
327  calibrator.pixelCalibrator. template connect<static_cast<CalibFuncPtr_t>(&AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate)>(this);
328 }
329 
330 template <typename calib_data_t, typename traj_t>
331 void AnalogueClusteringToolImpl<calib_data_t, traj_t>::connectPixelCalibrator(IOnBoundStateCalibratorTool::PixelCalibrator& pixelCalibrator) const
332 {
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 Acts::BoundTrackParameters&) const;
340 
341  pixelCalibrator.template connect<static_cast<CalibFuncPtr_t>(&AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrate)>(this);
342 }
343 
344 template <typename calib_data_t, typename traj_t>
345 bool AnalogueClusteringToolImpl<calib_data_t, traj_t>::calibrateAfterMeasurementSelection() const
346 { return m_postCalibration.value(); }
347 
348 } // namespace ActsTrk
349 
350 #endif