ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
AnnulusBounds.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file AnnulusBounds.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2020 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
13 
14 #include <cmath>
15 #include <iomanip>
16 #include <iostream>
17 
19  const std::array<double, eSize>& values) noexcept(false)
20  : m_values(values), m_moduleOrigin({values[eOriginX], values[eOriginY]}) {
21  checkConsistency();
22 
24  Eigen::Translation<double, 2>(Vector2D(0, -get(eAveragePhi)));
25  m_translation = Eigen::Translation<double, 2>(m_moduleOrigin);
26 
27  m_shiftXY = m_moduleOrigin * -1;
30 
31  // we need the corner points of the module to do the inside
32  // checking, calculate them here once, they don't change
33 
34  // find inner outer radius at edges in STRIP PC
35  auto circIx = [](double O_x, double O_y, double r, double phi) -> Vector2D {
36  // _____________________________________________
37  // / 2 2 2 2 2 2
38  // O_x + O_y*m - \/ - O_x *m + 2*O_x*O_y*m - O_y + m *r + r
39  // x = --------------------------------------------------------------
40  // 2
41  // m + 1
42  //
43  // y = m*x
44  //
45  double m = std::tan(phi);
46  Vector2D dir(std::cos(phi), std::sin(phi));
47  double x1 = (O_x + O_y * m -
48  std::sqrt(-std::pow(O_x, 2) * std::pow(m, 2) +
49  2 * O_x * O_y * m - std::pow(O_y, 2) +
50  std::pow(m, 2) * std::pow(r, 2) + std::pow(r, 2))) /
51  (std::pow(m, 2) + 1);
52  double x2 = (O_x + O_y * m +
53  std::sqrt(-std::pow(O_x, 2) * std::pow(m, 2) +
54  2 * O_x * O_y * m - std::pow(O_y, 2) +
55  std::pow(m, 2) * std::pow(r, 2) + std::pow(r, 2))) /
56  (std::pow(m, 2) + 1);
57 
58  Vector2D v1(x1, m * x1);
59  if (v1.dot(dir) > 0)
60  return v1;
61  return {x2, m * x2};
62  };
63 
64  // calculate corners in STRIP XY, keep them we need them for minDistance()
65  m_outLeftStripXY = circIx(m_moduleOrigin[eLOC_X], m_moduleOrigin[eLOC_Y],
66  get(eMaxR), get(eMaxPhiRel));
67  m_inLeftStripXY = circIx(m_moduleOrigin[eLOC_X], m_moduleOrigin[eLOC_Y],
68  get(eMinR), get(eMaxPhiRel));
69  m_outRightStripXY = circIx(m_moduleOrigin[eLOC_X], m_moduleOrigin[eLOC_Y],
70  get(eMaxR), get(eMinPhiRel));
71  m_inRightStripXY = circIx(m_moduleOrigin[eLOC_X], m_moduleOrigin[eLOC_Y],
72  get(eMinR), get(eMinPhiRel));
73 
82 
83  m_outLeftModulePC = stripXYToModulePC(m_outLeftStripXY);
84  m_inLeftModulePC = stripXYToModulePC(m_inLeftStripXY);
85  m_outRightModulePC = stripXYToModulePC(m_outRightStripXY);
86  m_inRightModulePC = stripXYToModulePC(m_inRightStripXY);
87 }
88 
89 std::vector<Acts::Vector2D> Acts::AnnulusBounds::corners() const {
90  auto rot = m_rotationStripPC.inverse();
91 
92  return {rot * m_outRightStripPC, rot * m_outLeftStripPC,
93  rot * m_inLeftStripPC, rot * m_inRightStripPC};
94 }
95 
96 std::vector<Acts::Vector2D> Acts::AnnulusBounds::vertices(
97  unsigned int lseg) const {
98  // List of vertices counter-clockwise starting with left inner
99  std::vector<Acts::Vector2D> rvertices;
100 
101  double phiMinInner = VectorHelpers::phi(m_inLeftStripXY);
102  double phiMaxInner = VectorHelpers::phi(m_inRightStripXY);
103  double phiMinOuter = VectorHelpers::phi(m_outRightStripXY);
104  double phiMaxOuter = VectorHelpers::phi(m_outLeftStripXY);
105 
106  std::vector<double> phisInner =
107  detail::VerticesHelper::phiSegments(phiMinInner, phiMaxInner);
108  std::vector<double> phisOuter =
109  detail::VerticesHelper::phiSegments(phiMinOuter, phiMaxOuter);
110 
111  // Inner bow from phi_min -> phi_max
112  for (unsigned int iseg = 0; iseg < phisInner.size() - 1; ++iseg) {
113  int addon = (iseg == phisInner.size() - 2) ? 1 : 0;
114  detail::VerticesHelper::createSegment<Vector2D, Eigen::Affine2d>(
115  rvertices, {get(eMinR), get(eMinR)}, phisInner[iseg],
116  phisInner[iseg + 1], lseg, addon);
117  }
118  // Upper bow from phi_min -> phi_max
119  for (unsigned int iseg = 0; iseg < phisOuter.size() - 1; ++iseg) {
120  int addon = (iseg == phisOuter.size() - 2) ? 1 : 0;
121  detail::VerticesHelper::createSegment<Vector2D, Eigen::Affine2d>(
122  rvertices, {get(eMaxR), get(eMaxR)}, phisOuter[iseg],
123  phisOuter[iseg + 1], lseg, addon);
124  }
125 
126  return rvertices;
127 }
128 
129 bool Acts::AnnulusBounds::inside(const Vector2D& lposition, double tolR,
130  double tolPhi) const {
131  // locpo is PC in STRIP SYSTEM
132  // need to perform internal rotation induced by m_phiAvg
133  Vector2D locpo_rotated = m_rotationStripPC * lposition;
134  double phiLoc = locpo_rotated[eLOC_PHI];
135  double rLoc = locpo_rotated[eLOC_R];
136 
137  if (phiLoc < (get(eMinPhiRel) - tolPhi) ||
138  phiLoc > (get(eMaxPhiRel) + tolPhi)) {
139  return false;
140  }
141 
142  // calculate R in MODULE SYSTEM to evaluate R-bounds
143  if (tolR == 0.) {
144  // don't need R, can use R^2
145  double r_mod2 =
146  m_shiftPC[eLOC_R] * m_shiftPC[eLOC_R] + rLoc * rLoc +
147  2 * m_shiftPC[eLOC_R] * rLoc * cos(phiLoc - m_shiftPC[eLOC_PHI]);
148 
149  if (r_mod2 < get(eMinR) * get(eMinR) || r_mod2 > get(eMaxR) * get(eMaxR)) {
150  return false;
151  }
152  } else {
153  // use R
154  double r_mod =
155  sqrt(m_shiftPC[eLOC_R] * m_shiftPC[eLOC_R] + rLoc * rLoc +
156  2 * m_shiftPC[eLOC_R] * rLoc * cos(phiLoc - m_shiftPC[eLOC_PHI]));
157 
158  if (r_mod < (get(eMinR) - tolR) || r_mod > (get(eMaxR) + tolR)) {
159  return false;
160  }
161  }
162  return true;
163 }
164 
165 bool Acts::AnnulusBounds::inside(const Vector2D& lposition,
166  const BoundaryCheck& bcheck) const {
167  // locpo is PC in STRIP SYSTEM
168  if (bcheck.type() == BoundaryCheck::Type::eAbsolute) {
169  return inside(lposition, bcheck.tolerance()[eLOC_R],
170  bcheck.tolerance()[eLOC_PHI]);
171  } else {
172  // first check if inside. We don't need to look into the covariance if
173  // inside
174  if (inside(lposition, 0., 0.)) {
175  return true;
176  }
177 
178  // we need to rotated the locpo
179  Vector2D locpo_rotated = m_rotationStripPC * lposition;
180 
181  // covariance is given in STRIP SYSTEM in PC
182  // we need to convert the covariance to the MODULE SYSTEM in PC
183  // via jacobian.
184  // The following transforms into STRIP XY, does the shift into MODULE XY,
185  // and then transforms into MODULE PC
186  double dphi = m_phiAvg;
187  double phi_strip = locpo_rotated[eLOC_PHI];
188  double r_strip = locpo_rotated[eLOC_R];
189  double O_x = m_shiftXY[eLOC_X];
190  double O_y = m_shiftXY[eLOC_Y];
191 
192  // For a transformation from cartesian into polar coordinates
193  //
194  // [ _________ ]
195  // [ / 2 2 ]
196  // [ \/ x + y ]
197  // [ r' ] [ ]
198  // v = [ ] = [ / y \]
199  // [phi'] [2*atan|----------------|]
200  // [ | _________|]
201  // [ | / 2 2 |]
202  // [ \x + \/ x + y /]
203  //
204  // Where x, y are polar coordinates that can be rotated by dPhi
205  //
206  // [x] [O_x + r*cos(dPhi - phi)]
207  // [ ] = [ ]
208  // [y] [O_y - r*sin(dPhi - phi)]
209  //
210  // The general jacobian is:
211  //
212  // [d d ]
213  // [--(f_x) --(f_x)]
214  // [dx dy ]
215  // Jgen = [ ]
216  // [d d ]
217  // [--(f_y) --(f_y)]
218  // [dx dy ]
219  //
220  // which means in this case:
221  //
222  // [ d d ]
223  // [ ----------(rMod) ---------(rMod) ]
224  // [ dr_{strip} dphiStrip ]
225  // J = [ ]
226  // [ d d ]
227  // [----------(phiMod) ---------(phiMod)]
228  // [dr_{strip} dphiStrip ]
229  //
230  // Performing the derivative one gets:
231  //
232  // [B*O_x + C*O_y + rStrip rStrip*(B*O_y + O_x*sin(dPhi - phiStrip))]
233  // [---------------------- -----------------------------------------]
234  // [ ___ ___ ]
235  // [ \/ A \/ A ]
236  // J = [ ]
237  // [ -(B*O_y - C*O_x) rStrip*(B*O_x + C*O_y + rStrip) ]
238  // [ ----------------- ------------------------------- ]
239  // [ A A ]
240  //
241  // where
242  // 2 2 2
243  // A = O_x + 2*O_x*rStrip*cos(dPhi - phiStrip) + O_y -
244  // 2*O_y*rStrip*sin(dPhi - phiStrip) + rStrip B = cos(dPhi - phiStrip) C =
245  // -sin(dPhi - phiStrip)
246 
247  double cosDPhiPhiStrip = std::cos(dphi - phi_strip);
248  double sinDPhiPhiStrip = std::sin(dphi - phi_strip);
249 
250  double A = O_x * O_x + 2 * O_x * r_strip * cosDPhiPhiStrip + O_y * O_y -
251  2 * O_y * r_strip * sinDPhiPhiStrip + r_strip * r_strip;
252  double sqrtA = std::sqrt(A);
253 
254  double B = cosDPhiPhiStrip;
255  double C = -sinDPhiPhiStrip;
256  Eigen::Matrix<double, 2, 2> jacobianStripPCToModulePC;
257  jacobianStripPCToModulePC(0, 0) = (B * O_x + C * O_y + r_strip) / sqrtA;
258  jacobianStripPCToModulePC(0, 1) =
259  r_strip * (B * O_y + O_x * sinDPhiPhiStrip) / sqrtA;
260  jacobianStripPCToModulePC(1, 0) = -(B * O_y - C * O_x) / A;
261  jacobianStripPCToModulePC(1, 1) =
262  r_strip * (B * O_x + C * O_y + r_strip) / A;
263 
264  // covariance is given in STRIP PC
265  auto covStripPC = bcheck.covariance();
266  // calculate covariance in MODULE PC using jacobian from above
267  auto covModulePC = jacobianStripPCToModulePC * covStripPC *
268  jacobianStripPCToModulePC.transpose();
269 
270  // Mahalanobis distance uses inverse covariance as weights
271  auto weightStripPC = covStripPC.inverse();
272  auto weightModulePC = covModulePC.inverse();
273 
274  double minDist = std::numeric_limits<double>::max();
275 
276  Vector2D currentClosest;
277  double currentDist;
278 
279  // do projection in STRIP PC
280 
281  // first: STRIP system. locpo is in STRIP PC already
282  currentClosest = closestOnSegment(m_inLeftStripPC, m_outLeftStripPC,
283  locpo_rotated, weightStripPC);
284  currentDist = squaredNorm(locpo_rotated - currentClosest, weightStripPC);
285  minDist = currentDist;
286 
287  currentClosest = closestOnSegment(m_inRightStripPC, m_outRightStripPC,
288  locpo_rotated, weightStripPC);
289  currentDist = squaredNorm(locpo_rotated - currentClosest, weightStripPC);
290  if (currentDist < minDist) {
291  minDist = currentDist;
292  }
293 
294  // now: MODULE system. Need to transform locpo to MODULE PC
295  // transform is STRIP PC -> STRIP XY -> MODULE XY -> MODULE PC
296  Vector2D locpoStripXY(
297  locpo_rotated[eLOC_R] * std::cos(locpo_rotated[eLOC_PHI]),
298  locpo_rotated[eLOC_R] * std::sin(locpo_rotated[eLOC_PHI]));
299  Vector2D locpoModulePC = stripXYToModulePC(locpoStripXY);
300 
301  // now check edges in MODULE PC (inner and outer circle)
302  // assuming Mahalanobis distances are of same unit if covariance
303  // is correctly transformed
304  currentClosest = closestOnSegment(m_inLeftModulePC, m_inRightModulePC,
305  locpoModulePC, weightModulePC);
306  currentDist = squaredNorm(locpoModulePC - currentClosest, weightModulePC);
307  if (currentDist < minDist) {
308  minDist = currentDist;
309  }
310 
311  currentClosest = closestOnSegment(m_outLeftModulePC, m_outRightModulePC,
312  locpoModulePC, weightModulePC);
313  currentDist = squaredNorm(locpoModulePC - currentClosest, weightModulePC);
314  if (currentDist < minDist) {
315  minDist = currentDist;
316  }
317 
318  // compare resulting Mahalanobis distance to configured
319  // "number of sigmas"
320  // we square it b/c we never took the square root of the distance
321  return minDist < bcheck.tolerance()[0] * bcheck.tolerance()[0];
322  }
323 }
324 
326  const Vector2D& lposition) const {
327  // find the closest point on all edges, calculate distance
328  // return smallest one
329  // closest distance is cartesian, we want the result in mm.
330 
331  Vector2D locpo_rotated = m_rotationStripPC * lposition;
332 
333  // locpo is given in STRIP PC, we need it in STRIP XY and possibly MODULE XY
334  double rStrip = locpo_rotated[eLOC_R];
335  double phiStrip = locpo_rotated[eLOC_PHI];
336  Vector2D locpoStripXY(rStrip * std::cos(phiStrip),
337  rStrip * std::sin(phiStrip));
338  Vector2D locpoModuleXY = locpoStripXY + m_shiftXY;
339  double rMod = locpoModuleXY.norm();
340  double phiMod = VectorHelpers::phi(locpoModuleXY);
341 
342  Vector2D closestStripPC;
343  double minDist = std::numeric_limits<double>::max();
344  double curDist;
345 
346  // for rmin
347  if (m_inRightModulePC[eLOC_PHI] <= phiMod &&
348  phiMod < m_inLeftModulePC[eLOC_PHI]) {
349  // is inside phi bounds, to comparison to rmin and r max
350  // r min
351  curDist = std::abs(get(eMinR) - rMod);
352  if (curDist < minDist) {
353  minDist = curDist;
354  }
355  } else {
356  // is outside phi bounds, closest can only be the edge points here
357 
358  // in left
359  curDist = (m_inLeftStripXY - locpoStripXY).norm();
360  if (curDist < minDist) {
361  minDist = curDist;
362  }
363 
364  // in right
365  curDist = (m_inRightStripXY - locpoStripXY).norm();
366  if (curDist < minDist) {
367  minDist = curDist;
368  }
369  }
370 
371  if (get(eMinPhiRel) <= phiStrip && phiStrip < get(eMaxPhiRel)) {
372  // r max
373  curDist = std::abs(get(eMaxR) - rMod);
374  if (curDist < minDist) {
375  minDist = curDist;
376  }
377  } else {
378  // out left
379  curDist = (m_outLeftStripXY - locpoStripXY).norm();
380  if (curDist < minDist) {
381  minDist = curDist;
382  }
383 
384  // out right
385  curDist = (m_outRightStripXY - locpoStripXY).norm();
386  if (curDist < minDist) {
387  minDist = curDist;
388  }
389  }
390 
392 
393  // phi left
394  Vector2D closestLeft =
395  closestOnSegment(m_inLeftStripXY, m_outLeftStripXY, locpoStripXY, weight);
396  curDist = (closestLeft - locpoStripXY).norm();
397  if (curDist < minDist) {
398  minDist = curDist;
399  }
400 
401  // phi right
402  Vector2D closestRight = closestOnSegment(m_inRightStripXY, m_outRightStripXY,
403  locpoStripXY, weight);
404  curDist = (closestRight - locpoStripXY).norm();
405  if (curDist < minDist) {
406  minDist = curDist;
407  }
408 
409  return minDist;
410 }
411 
413  const Vector2D& vStripXY) const {
414  Vector2D vecModuleXY = vStripXY + m_shiftXY;
415  return {vecModuleXY.norm(), VectorHelpers::phi(vecModuleXY)};
416 }
417 
419  const Vector2D& a, const Vector2D& b, const Vector2D& p,
420  const Eigen::Matrix<double, 2, 2>& weight) const {
421  // connecting vector
422  auto n = b - a;
423  // squared norm of line
424  auto f = (n.transpose() * weight * n).value();
425  // weighted scalar product of line to point and segment line
426  auto u = ((p - a).transpose() * weight * n).value() / f;
427  // clamp to [0, 1], convert to point
428  return std::min(std::max(u, 0.0), 1.0) * n + a;
429 }
430 
432  const Vector2D& v, const Eigen::Matrix<double, 2, 2>& weight) const {
433  return (v.transpose() * weight * v).value();
434 }
435 
437  return Eigen::Rotation2D<double>(m_phiAvg) * m_moduleOrigin;
438 }
439 
440 // Ostream operator overload
441 std::ostream& Acts::AnnulusBounds::toStream(std::ostream& sl) const {
442  sl << std::setiosflags(std::ios::fixed);
443  sl << std::setprecision(7);
444  sl << "Acts::AnnulusBounds: (innerRadius, outerRadius, minPhi, maxPhi) = ";
445  sl << "(" << get(eMinR) << ", " << get(eMaxR) << ", " << phiMin() << ", "
446  << phiMax() << ")" << '\n';
447  sl << " - shift xy = " << m_shiftXY.x() << ", " << m_shiftXY.y() << '\n';
448  ;
449  sl << " - shift pc = " << m_shiftPC.x() << ", " << m_shiftPC.y() << '\n';
450  ;
451  sl << std::setprecision(-1);
452  return sl;
453 }