Analysis Software
Documentation for sPHENIX simulation 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 
10 
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <iomanip>
19 #include <iostream>
20 #include <limits>
21 
23  const std::array<double, eSize>& values) noexcept(false)
24  : m_values(values), m_moduleOrigin({values[eOriginX], values[eOriginY]}) {
25  checkConsistency();
26  m_rotationStripPC = Translation2(Vector2(0, -get(eAveragePhi)));
27  m_translation = Translation2(m_moduleOrigin);
28 
29  m_shiftXY = m_moduleOrigin * -1;
32 
33  // we need the corner points of the module to do the inside
34  // checking, calculate them here once, they don't change
35 
36  // find inner outer radius at edges in STRIP PC
37  auto circIx = [](double O_x, double O_y, double r, double phi) -> Vector2 {
38  // _____________________________________________
39  // / 2 2 2 2 2 2
40  // O_x + O_y*m - \/ - O_x *m + 2*O_x*O_y*m - O_y + m *r + r
41  // x = --------------------------------------------------------------
42  // 2
43  // m + 1
44  //
45  // y = m*x
46  //
47  double m = std::tan(phi);
48  Vector2 dir(std::cos(phi), std::sin(phi));
49  double x1 = (O_x + O_y * m -
50  std::sqrt(-std::pow(O_x, 2) * std::pow(m, 2) +
51  2 * O_x * O_y * m - std::pow(O_y, 2) +
52  std::pow(m, 2) * std::pow(r, 2) + std::pow(r, 2))) /
53  (std::pow(m, 2) + 1);
54  double x2 = (O_x + O_y * m +
55  std::sqrt(-std::pow(O_x, 2) * std::pow(m, 2) +
56  2 * O_x * O_y * m - std::pow(O_y, 2) +
57  std::pow(m, 2) * std::pow(r, 2) + std::pow(r, 2))) /
58  (std::pow(m, 2) + 1);
59 
60  Vector2 v1(x1, m * x1);
61  if (v1.dot(dir) > 0) {
62  return v1;
63  }
64  return {x2, m * x2};
65  };
66 
67  // calculate corners in STRIP XY, keep them we need them for minDistance()
69  circIx(m_moduleOrigin[eBoundLoc0], m_moduleOrigin[eBoundLoc1], get(eMaxR),
70  get(eMaxPhiRel));
72  circIx(m_moduleOrigin[eBoundLoc0], m_moduleOrigin[eBoundLoc1], get(eMinR),
73  get(eMaxPhiRel));
75  circIx(m_moduleOrigin[eBoundLoc0], m_moduleOrigin[eBoundLoc1], get(eMaxR),
76  get(eMinPhiRel));
78  circIx(m_moduleOrigin[eBoundLoc0], m_moduleOrigin[eBoundLoc1], get(eMinR),
79  get(eMinPhiRel));
80 
89 
90  m_outLeftModulePC = stripXYToModulePC(m_outLeftStripXY);
91  m_inLeftModulePC = stripXYToModulePC(m_inLeftStripXY);
92  m_outRightModulePC = stripXYToModulePC(m_outRightStripXY);
93  m_inRightModulePC = stripXYToModulePC(m_inRightStripXY);
94 }
95 
96 std::vector<Acts::Vector2> Acts::AnnulusBounds::corners() const {
97  auto rot = m_rotationStripPC.inverse();
98 
99  return {rot * m_outRightStripPC, rot * m_outLeftStripPC,
100  rot * m_inLeftStripPC, rot * m_inRightStripPC};
101 }
102 
103 std::vector<Acts::Vector2> Acts::AnnulusBounds::vertices(
104  unsigned int lseg) const {
105  if (lseg > 0) {
106  // List of vertices counter-clockwise starting with left inner
107  std::vector<Acts::Vector2> rvertices;
108 
109  using VectorHelpers::phi;
110  auto phisInner = detail::VerticesHelper::phiSegments(
111  phi(m_inRightStripXY - m_moduleOrigin),
112  phi(m_inLeftStripXY - m_moduleOrigin));
113  auto phisOuter = detail::VerticesHelper::phiSegments(
114  phi(m_outLeftStripXY - m_moduleOrigin),
115  phi(m_outRightStripXY - m_moduleOrigin));
116 
117  // Inner bow from phi_min -> phi_max
118  for (unsigned int iseg = 0; iseg < phisInner.size() - 1; ++iseg) {
119  int addon = (iseg == phisInner.size() - 2) ? 1 : 0;
120  detail::VerticesHelper::createSegment<Vector2, Transform2>(
121  rvertices, {get(eMinR), get(eMinR)}, phisInner[iseg],
122  phisInner[iseg + 1], lseg, addon);
123  }
124  // Upper bow from phi_max -> phi_min
125  for (unsigned int iseg = 0; iseg < phisOuter.size() - 1; ++iseg) {
126  int addon = (iseg == phisOuter.size() - 2) ? 1 : 0;
127  detail::VerticesHelper::createSegment<Vector2, Transform2>(
128  rvertices, {get(eMaxR), get(eMaxR)}, phisOuter[iseg],
129  phisOuter[iseg + 1], lseg, addon);
130  }
131  std::for_each(rvertices.begin(), rvertices.end(),
132  [&](Acts::Vector2& rv) { rv += m_moduleOrigin; });
133  return rvertices;
134  }
137 }
138 
139 bool Acts::AnnulusBounds::inside(const Vector2& lposition, double tolR,
140  double tolPhi) const {
141  // locpo is PC in STRIP SYSTEM
142  // need to perform internal rotation induced by average phi
143  Vector2 locpo_rotated = m_rotationStripPC * lposition;
144  double phiLoc = locpo_rotated[eBoundLoc1];
145  double rLoc = locpo_rotated[eBoundLoc0];
146 
147  if (phiLoc < (get(eMinPhiRel) - tolPhi) ||
148  phiLoc > (get(eMaxPhiRel) + tolPhi)) {
149  return false;
150  }
151 
152  // calculate R in MODULE SYSTEM to evaluate R-bounds
153  if (tolR == 0.) {
154  // don't need R, can use R^2
155  double r_mod2 =
156  m_shiftPC[eBoundLoc0] * m_shiftPC[eBoundLoc0] + rLoc * rLoc +
157  2 * m_shiftPC[eBoundLoc0] * rLoc * cos(phiLoc - m_shiftPC[eBoundLoc1]);
158 
159  if (r_mod2 < get(eMinR) * get(eMinR) || r_mod2 > get(eMaxR) * get(eMaxR)) {
160  return false;
161  }
162  } else {
163  // use R
164  double r_mod = sqrt(
165  m_shiftPC[eBoundLoc0] * m_shiftPC[eBoundLoc0] + rLoc * rLoc +
166  2 * m_shiftPC[eBoundLoc0] * rLoc * cos(phiLoc - m_shiftPC[eBoundLoc1]));
167 
168  if (r_mod < (get(eMinR) - tolR) || r_mod > (get(eMaxR) + tolR)) {
169  return false;
170  }
171  }
172  return true;
173 }
174 
175 bool Acts::AnnulusBounds::inside(const Vector2& lposition,
176  const BoundaryCheck& bcheck) const {
177  // locpo is PC in STRIP SYSTEM
178  if (bcheck.type() == BoundaryCheck::Type::eAbsolute) {
179  return inside(lposition, bcheck.tolerance()[eBoundLoc0],
180  bcheck.tolerance()[eBoundLoc1]);
181  } else {
182  // first check if inside. We don't need to look into the covariance if
183  // inside
184  if (inside(lposition, 0., 0.)) {
185  return true;
186  }
187 
188  // we need to rotate the locpo
189  Vector2 locpo_rotated = m_rotationStripPC * lposition;
190 
191  // covariance is given in STRIP SYSTEM in PC
192  // we need to convert the covariance to the MODULE SYSTEM in PC
193  // via jacobian.
194  // The following transforms into STRIP XY, does the shift into MODULE XY,
195  // and then transforms into MODULE PC
196  double dphi = get(eAveragePhi);
197  double phi_strip = locpo_rotated[eBoundLoc1];
198  double r_strip = locpo_rotated[eBoundLoc0];
199  double O_x = m_shiftXY[eBoundLoc0];
200  double O_y = m_shiftXY[eBoundLoc1];
201 
202  // For a transformation from cartesian into polar coordinates
203  //
204  // [ _________ ]
205  // [ / 2 2 ]
206  // [ \/ x + y ]
207  // [ r' ] [ ]
208  // v = [ ] = [ / y \]
209  // [phi'] [2*atan|----------------|]
210  // [ | _________|]
211  // [ | / 2 2 |]
212  // [ \x + \/ x + y /]
213  //
214  // Where x, y are polar coordinates that can be rotated by dPhi
215  //
216  // [x] [O_x + r*cos(dPhi - phi)]
217  // [ ] = [ ]
218  // [y] [O_y - r*sin(dPhi - phi)]
219  //
220  // The general jacobian is:
221  //
222  // [d d ]
223  // [--(f_x) --(f_x)]
224  // [dx dy ]
225  // Jgen = [ ]
226  // [d d ]
227  // [--(f_y) --(f_y)]
228  // [dx dy ]
229  //
230  // which means in this case:
231  //
232  // [ d d ]
233  // [ ----------(rMod) ---------(rMod) ]
234  // [ dr_{strip} dphiStrip ]
235  // J = [ ]
236  // [ d d ]
237  // [----------(phiMod) ---------(phiMod)]
238  // [dr_{strip} dphiStrip ]
239  //
240  // Performing the derivative one gets:
241  //
242  // [B*O_x + C*O_y + rStrip rStrip*(B*O_y + O_x*sin(dPhi - phiStrip))]
243  // [---------------------- -----------------------------------------]
244  // [ ___ ___ ]
245  // [ \/ A \/ A ]
246  // J = [ ]
247  // [ -(B*O_y - C*O_x) rStrip*(B*O_x + C*O_y + rStrip) ]
248  // [ ----------------- ------------------------------- ]
249  // [ A A ]
250  //
251  // where
252  // 2 2 2
253  // A = O_x + 2*O_x*rStrip*cos(dPhi - phiStrip) + O_y -
254  // 2*O_y*rStrip*sin(dPhi - phiStrip) + rStrip B = cos(dPhi - phiStrip) C =
255  // -sin(dPhi - phiStrip)
256 
257  double cosDPhiPhiStrip = std::cos(dphi - phi_strip);
258  double sinDPhiPhiStrip = std::sin(dphi - phi_strip);
259 
260  double A = O_x * O_x + 2 * O_x * r_strip * cosDPhiPhiStrip + O_y * O_y -
261  2 * O_y * r_strip * sinDPhiPhiStrip + r_strip * r_strip;
262  double sqrtA = std::sqrt(A);
263 
264  double B = cosDPhiPhiStrip;
265  double C = -sinDPhiPhiStrip;
266  ActsMatrix<2, 2> jacobianStripPCToModulePC;
267  jacobianStripPCToModulePC(0, 0) = (B * O_x + C * O_y + r_strip) / sqrtA;
268  jacobianStripPCToModulePC(0, 1) =
269  r_strip * (B * O_y + O_x * sinDPhiPhiStrip) / sqrtA;
270  jacobianStripPCToModulePC(1, 0) = -(B * O_y - C * O_x) / A;
271  jacobianStripPCToModulePC(1, 1) =
272  r_strip * (B * O_x + C * O_y + r_strip) / A;
273 
274  // covariance is given in STRIP PC
275  auto covStripPC = bcheck.covariance();
276  // calculate covariance in MODULE PC using jacobian from above
277  auto covModulePC = jacobianStripPCToModulePC * covStripPC *
278  jacobianStripPCToModulePC.transpose();
279 
280  // Mahalanobis distance uses inverse covariance as weights
281  auto weightStripPC = covStripPC.inverse();
282  auto weightModulePC = covModulePC.inverse();
283 
284  double minDist = std::numeric_limits<double>::max();
285 
286  Vector2 currentClosest;
287  double currentDist = 0;
288 
289  // do projection in STRIP PC
290 
291  // first: STRIP system. locpo is in STRIP PC already
292  currentClosest = closestOnSegment(m_inLeftStripPC, m_outLeftStripPC,
293  locpo_rotated, weightStripPC);
294  currentDist = squaredNorm(locpo_rotated - currentClosest, weightStripPC);
295  minDist = currentDist;
296 
297  currentClosest = closestOnSegment(m_inRightStripPC, m_outRightStripPC,
298  locpo_rotated, weightStripPC);
299  currentDist = squaredNorm(locpo_rotated - currentClosest, weightStripPC);
300  if (currentDist < minDist) {
301  minDist = currentDist;
302  }
303 
304  // now: MODULE system. Need to transform locpo to MODULE PC
305  // transform is STRIP PC -> STRIP XY -> MODULE XY -> MODULE PC
306  Vector2 locpoStripXY(
307  locpo_rotated[eBoundLoc0] * std::cos(locpo_rotated[eBoundLoc1]),
308  locpo_rotated[eBoundLoc0] * std::sin(locpo_rotated[eBoundLoc1]));
309  Vector2 locpoModulePC = stripXYToModulePC(locpoStripXY);
310 
311  // now check edges in MODULE PC (inner and outer circle)
312  // assuming Mahalanobis distances are of same unit if covariance
313  // is correctly transformed
314  currentClosest = closestOnSegment(m_inLeftModulePC, m_inRightModulePC,
315  locpoModulePC, weightModulePC);
316  currentDist = squaredNorm(locpoModulePC - currentClosest, weightModulePC);
317  if (currentDist < minDist) {
318  minDist = currentDist;
319  }
320 
321  currentClosest = closestOnSegment(m_outLeftModulePC, m_outRightModulePC,
322  locpoModulePC, weightModulePC);
323  currentDist = squaredNorm(locpoModulePC - currentClosest, weightModulePC);
324  if (currentDist < minDist) {
325  minDist = currentDist;
326  }
327 
328  // compare resulting Mahalanobis distance to configured
329  // "number of sigmas"
330  // we square it b/c we never took the square root of the distance
331  return minDist < bcheck.tolerance()[0] * bcheck.tolerance()[0];
332  }
333 }
334 
336  const Vector2& vStripXY) const {
337  Vector2 vecModuleXY = vStripXY + m_shiftXY;
338  return {vecModuleXY.norm(), VectorHelpers::phi(vecModuleXY)};
339 }
340 
342  const Vector2& a, const Vector2& b, const Vector2& p,
343  const SquareMatrix2& weight) const {
344  // connecting vector
345  auto n = b - a;
346  // squared norm of line
347  auto f = (n.transpose() * weight * n).value();
348  // weighted scalar product of line to point and segment line
349  auto u = ((p - a).transpose() * weight * n).value() / f;
350  // clamp to [0, 1], convert to point
351  return std::min(std::max(u, static_cast<ActsScalar>(0)),
352  static_cast<ActsScalar>(1)) *
353  n +
354  a;
355 }
356 
358  const SquareMatrix2& weight) const {
359  return (v.transpose() * weight * v).value();
360 }
361 
363  return Eigen::Rotation2D<ActsScalar>(get(eAveragePhi)) * m_moduleOrigin;
364 }
365 
366 // Ostream operator overload
367 std::ostream& Acts::AnnulusBounds::toStream(std::ostream& sl) const {
368  sl << std::setiosflags(std::ios::fixed);
369  sl << std::setprecision(7);
370  sl << "Acts::AnnulusBounds: (innerRadius, outerRadius, minPhi, maxPhi) = ";
371  sl << "(" << get(eMinR) << ", " << get(eMaxR) << ", " << phiMin() << ", "
372  << phiMax() << ")" << '\n';
373  sl << " - shift xy = " << m_shiftXY.x() << ", " << m_shiftXY.y() << '\n';
374  sl << " - shift pc = " << m_shiftPC.x() << ", " << m_shiftPC.y() << '\n';
375  sl << std::setprecision(-1);
376  return sl;
377 }