Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ConeSurface.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file ConeSurface.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2016-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 
20 
21 #include <algorithm>
22 #include <cmath>
23 #include <limits>
24 #include <stdexcept>
25 #include <utility>
26 #include <vector>
27 
30 
32  : GeometryObject(), Surface(other), m_bounds(other.m_bounds) {}
33 
35  const ConeSurface& other,
36  const Transform3& shift)
37  : GeometryObject(), Surface(gctx, other, shift), m_bounds(other.m_bounds) {}
38 
40  bool symmetric)
41  : GeometryObject(),
42  Surface(transform),
43  m_bounds(std::make_shared<const ConeBounds>(alpha, symmetric)) {}
44 
46  double zmin, double zmax, double halfPhi)
47  : GeometryObject(),
48  Surface(transform),
49  m_bounds(std::make_shared<const ConeBounds>(alpha, zmin, zmax, halfPhi)) {
50 }
51 
53  std::shared_ptr<const ConeBounds> cbounds)
54  : GeometryObject(), Surface(transform), m_bounds(std::move(cbounds)) {
55  throw_assert(m_bounds, "ConeBounds must not be nullptr");
56 }
57 
59  const GeometryContext& gctx, Acts::BinningValue bValue) const {
60  const Vector3& sfCenter = center(gctx);
61 
62  // special binning type for R-type methods
63  if (bValue == Acts::binR || bValue == Acts::binRPhi) {
64  return Vector3(sfCenter.x() + bounds().r(sfCenter.z()), sfCenter.y(),
65  sfCenter.z());
66  }
67  // give the center as default for all of these binning types
68  // binX, binY, binZ, binR, binPhi, binRPhi, binH, binEta
69  return sfCenter;
70 }
71 
73  return Surface::Cone;
74 }
75 
77  if (this != &other) {
78  Surface::operator=(other);
79  m_bounds = other.m_bounds;
80  }
81  return *this;
82 }
83 
85  const GeometryContext& gctx) const {
86  return transform(gctx).matrix().block<3, 1>(0, 2);
87 }
88 
90  const GeometryContext& gctx, const Vector3& position,
91  const Vector3& /*direction*/) const {
92  RotationMatrix3 mFrame;
93  // construct the measurement frame
94  // measured Y is the local z axis
95  Vector3 measY = rotSymmetryAxis(gctx);
96  // measured z is the position transverse normalized
97  Vector3 measDepth = Vector3(position.x(), position.y(), 0.).normalized();
98  // measured X is what comoes out of it
99  Acts::Vector3 measX(measY.cross(measDepth).normalized());
100  // the columnes
101  mFrame.col(0) = measX;
102  mFrame.col(1) = measY;
103  mFrame.col(2) = measDepth;
104  // return the rotation matrix
106  // return it
107  return mFrame;
108 }
109 
111  const GeometryContext& gctx, const Vector2& lposition,
112  const Vector3& /*direction*/) const {
113  // create the position in the local 3d frame
114  double r = lposition[Acts::eBoundLoc1] * bounds().tanAlpha();
115  double phi = lposition[Acts::eBoundLoc0] / r;
116  Vector3 loc3Dframe(r * cos(phi), r * sin(phi), lposition[Acts::eBoundLoc1]);
117  return transform(gctx) * loc3Dframe;
118 }
119 
121  const GeometryContext& gctx, const Vector3& position,
122  const Vector3& /*direction*/, double tolerance) const {
123  Vector3 loc3Dframe = transform(gctx).inverse() * position;
124  double r = loc3Dframe.z() * bounds().tanAlpha();
125  if (std::abs(perp(loc3Dframe) - r) > tolerance) {
126  return Result<Vector2>::failure(SurfaceError::GlobalPositionNotOnSurface);
127  }
129  Vector2(r * atan2(loc3Dframe.y(), loc3Dframe.x()), loc3Dframe.z()));
130 }
131 
133  const Vector3& position,
134  const Vector3& direction) const {
135  // (cos phi cos alpha, sin phi cos alpha, sgn z sin alpha)
136  Vector3 posLocal = transform(gctx).inverse() * position;
137  double phi = VectorHelpers::phi(posLocal);
138  double sgn = posLocal.z() > 0. ? -1. : +1.;
139  double cosAlpha = std::cos(bounds().get(ConeBounds::eAlpha));
140  double sinAlpha = std::sin(bounds().get(ConeBounds::eAlpha));
141  Vector3 normalC(cos(phi) * cosAlpha, sin(phi) * cosAlpha, sgn * sinAlpha);
142  normalC = transform(gctx) * normalC;
143  // Back to the global frame
144  double cAlpha = normalC.dot(direction);
145  return std::abs(1. / cAlpha);
146 }
147 
149  return "Acts::ConeSurface";
150 }
151 
153  const Acts::Vector2& lposition) const {
154  // (cos phi cos alpha, sin phi cos alpha, sgn z sin alpha)
155  double phi = lposition[Acts::eBoundLoc0] /
156  (bounds().r(lposition[Acts::eBoundLoc1])),
157  sgn = lposition[Acts::eBoundLoc1] > 0 ? -1. : +1.;
158  double cosAlpha = std::cos(bounds().get(ConeBounds::eAlpha));
159  double sinAlpha = std::sin(bounds().get(ConeBounds::eAlpha));
160  Vector3 localNormal(cos(phi) * cosAlpha, sin(phi) * cosAlpha, sgn * sinAlpha);
161  return Vector3(transform(gctx).linear() * localNormal);
162 }
163 
165  const Acts::Vector3& position) const {
166  // get it into the cylinder frame if needed
167  // @todo respect opening angle
168  Vector3 pos3D = transform(gctx).inverse() * position;
169  pos3D.z() = 0;
170  return pos3D.normalized();
171 }
172 
174  // is safe because no constructor w/o bounds exists
175  return (*m_bounds.get());
176 }
177 
179  const GeometryContext& gctx, size_t lseg) const {
180  // Prepare vertices and faces
181  std::vector<Vector3> vertices;
182  std::vector<Polyhedron::FaceType> faces;
183  std::vector<Polyhedron::FaceType> triangularMesh;
184 
185  double minZ = bounds().get(ConeBounds::eMinZ);
186  double maxZ = bounds().get(ConeBounds::eMaxZ);
187 
188  if (minZ == -std::numeric_limits<double>::infinity() or
189  maxZ == std::numeric_limits<double>::infinity()) {
190  throw std::domain_error(
191  "Polyhedron repr of boundless surface not possible");
192  }
193 
194  auto ctransform = transform(gctx);
195 
196  // The tip - created only once and only, if it is not a cut-off cone
197  bool tipExists = false;
198  if (minZ * maxZ <= s_onSurfaceTolerance) {
199  vertices.push_back(ctransform * Vector3(0., 0., 0.));
200  tipExists = true;
201  }
202 
203  // Cone parameters
204  double hPhiSec = bounds().get(ConeBounds::eHalfPhiSector);
205  double avgPhi = bounds().get(ConeBounds::eAveragePhi);
206  bool fullCone = (hPhiSec == M_PI);
207 
208  // Get the phi segments from the helper
209  auto phiSegs = fullCone ? detail::VerticesHelper::phiSegments()
211  avgPhi - hPhiSec, avgPhi + hPhiSec,
212  {static_cast<ActsScalar>(avgPhi)});
213 
214  // Negative cone if exists
215  std::vector<double> coneSides;
216  if (std::abs(minZ) > s_onSurfaceTolerance) {
217  coneSides.push_back(minZ);
218  }
219  if (std::abs(maxZ) > s_onSurfaceTolerance) {
220  coneSides.push_back(maxZ);
221  }
222  for (auto& z : coneSides) {
223  // Remember the first vertex
224  size_t firstIv = vertices.size();
225  // Radius and z offset
226  double r = std::abs(z) * bounds().tanAlpha();
227  Vector3 zoffset(0., 0., z);
228  for (unsigned int iseg = 0; iseg < phiSegs.size() - 1; ++iseg) {
229  int addon = (iseg == phiSegs.size() - 2 and not fullCone) ? 1 : 0;
230  detail::VerticesHelper::createSegment(vertices, {r, r}, phiSegs[iseg],
231  phiSegs[iseg + 1], lseg, addon,
232  zoffset, ctransform);
233  }
234  // Create the faces
235  if (tipExists) {
236  for (size_t iv = firstIv + 2; iv < vertices.size() + 1; ++iv) {
237  size_t one = 0, two = iv - 1, three = iv - 2;
238  if (z < 0.) {
239  std::swap(two, three);
240  }
241  faces.push_back({one, two, three});
242  }
243  // Complete cone if necessary
244  if (fullCone) {
245  if (z > 0.) {
246  faces.push_back({0, firstIv, vertices.size() - 1});
247  } else {
248  faces.push_back({0, vertices.size() - 1, firstIv});
249  }
250  }
251  }
252  }
253  // if no tip exists, connect the two bows
254  if (tipExists) {
255  triangularMesh = faces;
256  } else {
257  auto facesMesh =
258  detail::FacesHelper::cylindricalFaceMesh(vertices, fullCone);
259  faces = facesMesh.first;
260  triangularMesh = facesMesh.second;
261  }
262  return Polyhedron(vertices, faces, triangularMesh, false);
263 }
264 
266  const GeometryContext& gctx, const Vector3& position,
267  const Vector3& direction) const {
268  // Transform into the local frame
269  Transform3 invTrans = transform(gctx).inverse();
270  Vector3 point1 = invTrans * position;
271  Vector3 dir1 = invTrans.linear() * direction;
272 
273  // See file header for the formula derivation
274  double tan2Alpha = bounds().tanAlpha() * bounds().tanAlpha(),
275  A = dir1.x() * dir1.x() + dir1.y() * dir1.y() -
276  tan2Alpha * dir1.z() * dir1.z(),
277  B = 2 * (dir1.x() * point1.x() + dir1.y() * point1.y() -
278  tan2Alpha * dir1.z() * point1.z()),
279  C = point1.x() * point1.x() + point1.y() * point1.y() -
280  tan2Alpha * point1.z() * point1.z();
281  if (A == 0.) {
282  A += 1e-16; // avoid division by zero
283  }
284 
285  return detail::RealQuadraticEquation(A, B, C);
286 }
287 
289  const GeometryContext& gctx, const Vector3& position,
290  const Vector3& direction, const BoundaryCheck& bcheck,
291  ActsScalar tolerance) const {
292  // Solve the quadratic equation
293  auto qe = intersectionSolver(gctx, position, direction);
294 
295  // If no valid solution return a non-valid surfaceIntersection
296  if (qe.solutions == 0) {
297  return {{Intersection3D::invalid(), Intersection3D::invalid()}, this};
298  }
299 
300  // Check the validity of the first solution
301  Vector3 solution1 = position + qe.first * direction;
302  Intersection3D::Status status1 = std::abs(qe.first) < std::abs(tolerance)
303  ? Intersection3D::Status::onSurface
304  : Intersection3D::Status::reachable;
305 
306  if (bcheck and not isOnSurface(gctx, solution1, direction, bcheck)) {
307  status1 = Intersection3D::Status::missed;
308  }
309 
310  // Check the validity of the second solution
311  Vector3 solution2 = position + qe.first * direction;
312  Intersection3D::Status status2 = std::abs(qe.second) < std::abs(tolerance)
313  ? Intersection3D::Status::onSurface
314  : Intersection3D::Status::reachable;
315  if (bcheck and not isOnSurface(gctx, solution2, direction, bcheck)) {
316  status2 = Intersection3D::Status::missed;
317  }
318 
319  const auto& tf = transform(gctx);
320  // Set the intersection
321  Intersection3D first(tf * solution1, qe.first, status1);
322  Intersection3D second(tf * solution2, qe.second, status2);
323  // Order based on path length
324  if (first.pathLength() <= second.pathLength()) {
325  return {{first, second}, this};
326  }
327  return {{second, first}, this};
328 }
329 
331  const GeometryContext& gctx, const FreeVector& parameters) const {
332  // The global position
333  const auto position = parameters.segment<3>(eFreePos0);
334  // The direction
335  const auto direction = parameters.segment<3>(eFreeDir0);
336  // The vector between position and center
337  const auto pcRowVec = (position - center(gctx)).transpose().eval();
338  // The rotation
339  const auto& rotation = transform(gctx).rotation();
340  // The local frame x/y/z axis
341  const auto& localXAxis = rotation.col(0);
342  const auto& localYAxis = rotation.col(1);
343  const auto& localZAxis = rotation.col(2);
344  // The local coordinates
345  const auto localPos = (rotation.transpose() * position).eval();
346  const auto dx = direction.dot(localXAxis);
347  const auto dy = direction.dot(localYAxis);
348  const auto dz = direction.dot(localZAxis);
349  // The normalization factor
350  const auto tanAlpha2 = bounds().tanAlpha() * bounds().tanAlpha();
351  const auto norm = 1 / (1 - dz * dz * (1 + tanAlpha2));
352  // The direction transpose
353  const auto& dirRowVec = direction.transpose();
354  // The derivative of path w.r.t. the local axes
355  // @note The following calculations assume that the intersection of the track
356  // with the cone always satisfy: localPos.z()*tanAlpha =perp(localPos)
357  const auto localXAxisToPath =
358  (-2 * norm * (dx * pcRowVec + localPos.x() * dirRowVec)).eval();
359  const auto localYAxisToPath =
360  (-2 * norm * (dy * pcRowVec + localPos.y() * dirRowVec)).eval();
361  const auto localZAxisToPath =
362  (2 * norm * tanAlpha2 * (dz * pcRowVec + localPos.z() * dirRowVec) -
363  4 * norm * norm * (1 + tanAlpha2) *
364  (dx * localPos.x() + dy * localPos.y() -
365  dz * localPos.z() * tanAlpha2) *
366  dz * dirRowVec)
367  .eval();
368  // Calculate the derivative of local frame axes w.r.t its rotation
369  const auto [rotToLocalXAxis, rotToLocalYAxis, rotToLocalZAxis] =
371  // Initialize the derivative of propagation path w.r.t. local frame
372  // translation (origin) and rotation
373  AlignmentToPathMatrix alignToPath = AlignmentToPathMatrix::Zero();
374  alignToPath.segment<3>(eAlignmentCenter0) =
375  2 * norm * (dx * localXAxis.transpose() + dy * localYAxis.transpose());
376  alignToPath.segment<3>(eAlignmentRotation0) =
377  localXAxisToPath * rotToLocalXAxis + localYAxisToPath * rotToLocalYAxis +
378  localZAxisToPath * rotToLocalZAxis;
379 
380  return alignToPath;
381 }
382 
384  const GeometryContext& gctx, const Vector3& position) const {
385  using VectorHelpers::perp;
386  using VectorHelpers::phi;
387  // The local frame transform
388  const auto& sTransform = transform(gctx);
389  // calculate the transformation to local coordinates
390  const Vector3 localPos = sTransform.inverse() * position;
391  const double lr = perp(localPos);
392  const double lphi = phi(localPos);
393  const double lcphi = std::cos(lphi);
394  const double lsphi = std::sin(lphi);
395  // Solve for radius R
396  const double R = localPos.z() * bounds().tanAlpha();
397  ActsMatrix<2, 3> loc3DToLocBound = ActsMatrix<2, 3>::Zero();
398  loc3DToLocBound << -R * lsphi / lr, R * lcphi / lr,
399  lphi * bounds().tanAlpha(), 0, 0, 1;
400 
401  return loc3DToLocBound;
402 }