CMS 3D CMS Logo

HelixTrajectory.cc
Go to the documentation of this file.
8 #include <cmath>
9 
10 // helix phi definition
11 // ranges from 0 to 2PI
12 // 0 corresponds to the positive x direction
13 // phi increases counterclockwise
14 
16  : Trajectory(particle)
17  // exact: r = gamma*beta*m_0*c / (q*e*B) = p_T / (q * e * B)
18  // momentum in units of GeV/c: r = p_T * 10^9 / (c * q * B)
19  // in cmssw units: r = p_T / (c * 10^-4 * q * B)
20  ,
21  radius_(
22  std::abs(momentum_.Pt() / (fastsim::Constants::speedOfLight * 1e-4 * particle.charge() * magneticFieldZ))),
23  phi_(std::atan(momentum_.Py() / momentum_.Px()) +
24  (momentum_.Px() * particle.charge() < 0 ? 3. * M_PI / 2. : M_PI / 2.))
25  // maybe consider (for -pi/2<x<pi/2)
26  // cos(atan(x)) = 1 / sqrt(x^2+1)
27  // -> cos(atan(x) + pi/2) = - x / sqrt(x^2+1)
28  // -> cos(atan(x) +3*pi/2) = + x / sqrt(x^2+1)
29  // sin(atan(x)) = x / sqrt(x^2+1)
30  // -> sin(atan(x) + pi/2) = + 1 / sqrt(x^2+1)
31  // -> sin(atan(x) +3*pi/2) = - 1 / sqrt(x^2+1)
32  ,
33  centerX_(position_.X() -
34  radius_ * (momentum_.Py() / momentum_.Px()) /
35  std::sqrt((momentum_.Py() / momentum_.Px()) * (momentum_.Py() / momentum_.Px()) + 1) *
36  (momentum_.Px() * particle.charge() < 0 ? 1. : -1.)),
37  centerY_(position_.Y() -
38  radius_ * 1 / std::sqrt((momentum_.Py() / momentum_.Px()) * (momentum_.Py() / momentum_.Px()) + 1) *
39  (momentum_.Px() * particle.charge() < 0 ? -1. : 1.))
40  //, centerX_(position_.X() - radius_*std::cos(phi_))
41  //, centerY_(position_.Y() - radius_*std::sin(phi_))
42  ,
43  centerR_(std::sqrt(centerX_ * centerX_ + centerY_ * centerY_)),
44  minR_(std::abs(centerR_ - radius_)),
45  maxR_(centerR_ + radius_)
46  // omega = q * e * B / (gamma * m) = q * e *B / (E / c^2) = q * e * B * c^2 / E
47  // omega: negative for negative q -> seems to be what we want.
48  // energy in units of GeV: omega = q * B * c^2 / (E * 10^9)
49  // in cmssw units: omega[1/ns] = q * B * c^2 * 10^-4 / E
50  ,
51  phiSpeed_(-particle.charge() * magneticFieldZ * fastsim::Constants::speedOfLight *
52  fastsim::Constants::speedOfLight * 1e-4 / momentum_.E()) {
53  ;
54 }
55 
57  return (minR_ < layer.getRadius() && maxR_ > layer.getRadius());
58 }
59 
61  if (!crosses(layer))
62  return -1;
63 
64  // solve the following equation for sin(phi)
65  // (x^2 + y^2 = R_L^2) (1) the layer
66  // x = x_c + R_H*cos(phi) (2) the helix in the xy plane
67  // y = y_c + R_H*sin(phi) (3) the helix in the xy plane
68  // with
69  // R_L: the radius of the layer
70  // x_c,y_c the center of the helix in xy plane
71  // R_H, the radius of the helix
72  // phi, the phase of the helix
73  //
74  // substitute (2) and (3) in (1)
75  // =>
76  // x_c^2 + 2*x_c*R_H*cos(phi) + R_H^2*cos^2(phi)
77  // + y_c^2 + 2*y_c*R_H*sin(phi) + R_H^2*sin^2(phi)
78  // = R_L^2
79  // =>
80  // (x_c^2 + y_c^2 + R_H^2 - R_L^2) + (2*y_c*R_H)*sin(phi) = -(2*x_c*R_H)*cos(phi)
81  //
82  // rewrite
83  // E + F *sin(phi) = G *cos(phi)
84  // =>
85  // E^2 + 2*E*F*sin(phi) + F^2*sin^2(phi) = G^2*(1-sin^2(phi))
86  // rearrange
87  // sin^2(phi)*(F^2 + G^2) + sin(phi)*(2*E*F) + (E^2 - G^2) = 0
88  //
89  // rewrite
90  // sin^2(phi)* a + sin(phi)* b + c = 0
91  // => sin(phi) = (-b +/- sqrt(b^2 - 4*ac)) / (2*a)
92  // with
93  // a = F^2 + G^2
94  // b = 2*E*F
95  // c = E^2 - G^2
96 
97  double E = centerX_ * centerX_ + centerY_ * centerY_ + radius_ * radius_ - layer.getRadius() * layer.getRadius();
98  double F = 2 * centerY_ * radius_;
99  double G = 2 * centerX_ * radius_;
100 
101  double a = F * F + G * G;
102  double b = 2 * E * F;
103  double c = E * E - G * G;
104 
105  double delta = b * b - 4 * a * c;
106 
107  // case of no solution
108  if (delta < 0) {
109  // Should not be reached: Full Propagation does always have a solution "if(crosses(layer)) == -1"
110  // Even if particle is outside all layers -> can turn around in magnetic field
111  return -1;
112  }
113 
114  // Uses a numerically more stable procedure:
115  // https://people.csail.mit.edu/bkph/articles/Quadratics.pdf
116  double sqrtDelta = sqrt(delta);
117  double phi1 = 0, phi2 = 0;
118  if (b < 0) {
119  phi1 = std::asin((2. * c) / (-b + sqrtDelta));
120  phi2 = std::asin((-b + sqrtDelta) / (2. * a));
121  } else {
122  phi1 = std::asin((-b - sqrtDelta) / (2. * a));
123  phi2 = std::asin((2. * c) / (-b - sqrtDelta));
124  }
125 
126  // asin is ambiguous, make sure to have the right solution
127  if (std::abs(layer.getRadius() - getRadParticle(phi1)) > 1.0e-2) {
128  phi1 = -phi1 + M_PI;
129  }
130  if (std::abs(layer.getRadius() - getRadParticle(phi2)) > 1.0e-2) {
131  phi2 = -phi2 + M_PI;
132  }
133 
134  // another ambiguity
135  if (phi1 < 0) {
136  phi1 += 2. * M_PI;
137  }
138  if (phi2 < 0) {
139  phi2 += 2. * M_PI;
140  }
141 
142  // find the corresponding times when the intersection occurs
143  // make sure they are positive
144  double t1 = (phi1 - phi_) / phiSpeed_;
145  while (t1 < 0) {
146  t1 += 2 * M_PI / std::abs(phiSpeed_);
147  }
148  double t2 = (phi2 - phi_) / phiSpeed_;
149  while (t2 < 0) {
150  t2 += 2 * M_PI / std::abs(phiSpeed_);
151  }
152 
153  // Check if propagation successful (numerical reasons): both solutions (phi1, phi2) have to be on the layer (same radius)
154  // Can happen due to numerical instabilities of geometrical function (if momentum is almost parallel to x/y axis)
155  // Get crossingTimeC from StraightTrajectory as good approximation
156  if (std::abs(layer.getRadius() - getRadParticle(phi1)) > 1.0e-2 ||
157  std::abs(layer.getRadius() - getRadParticle(phi2)) > 1.0e-2) {
158  StraightTrajectory traj(*this);
159  return traj.nextCrossingTimeC(layer, onLayer);
160  }
161 
162  // if the particle is already on the layer, we need to make sure the 2nd solution is picked.
163  // happens if particle turns around in the magnetic field instead of hitting the next layer
164  if (onLayer) {
165  bool particleMovesInwards = momentum_.X() * position_.X() + momentum_.Y() * position_.Y() < 0;
166 
167  double posX1 = centerX_ + radius_ * std::cos(phi1);
168  double posY1 = centerY_ + radius_ * std::sin(phi1);
169  double momX1 = momentum_.X() * std::cos(phi1 - phi_) - momentum_.Y() * std::sin(phi1 - phi_);
170  double momY1 = momentum_.X() * std::sin(phi1 - phi_) + momentum_.Y() * std::cos(phi1 - phi_);
171  bool particleMovesInwards1 = momX1 * posX1 + momY1 * posY1 < 0;
172 
173  double posX2 = centerX_ + radius_ * std::cos(phi2);
174  double posY2 = centerY_ + radius_ * std::sin(phi2);
175  double momX2 = momentum_.X() * std::cos(phi2 - phi_) - momentum_.Y() * std::sin(phi2 - phi_);
176  double momY2 = momentum_.X() * std::sin(phi2 - phi_) + momentum_.Y() * std::cos(phi2 - phi_);
177  bool particleMovesInwards2 = momX2 * posX2 + momY2 * posY2 < 0;
178 
179  if (particleMovesInwards1 != particleMovesInwards) {
181  } else if (particleMovesInwards2 != particleMovesInwards) {
183  }
184  // try to catch numerical issues again..
185  else {
186  return -1;
187  }
188  }
189 
191 }
192 
193 void fastsim::HelixTrajectory::move(double deltaTimeC) {
194  double deltaT = deltaTimeC / fastsim::Constants::speedOfLight;
195  double deltaPhi = phiSpeed_ * deltaT;
196  position_.SetXYZT(centerX_ + radius_ * std::cos(phi_ + deltaPhi),
197  centerY_ + radius_ * std::sin(phi_ + deltaPhi),
198  position_.Z() + momentum_.Z() / momentum_.E() * deltaTimeC,
199  position_.T() + deltaT);
200  // Rotation defined by
201  // x' = x cos θ - y sin θ
202  // y' = x sin θ + y cos θ
203  momentum_.SetXYZT(momentum_.X() * std::cos(deltaPhi) - momentum_.Y() * std::sin(deltaPhi),
204  momentum_.X() * std::sin(deltaPhi) + momentum_.Y() * std::cos(deltaPhi),
205  momentum_.Z(),
206  momentum_.E());
207 }
208 
210  return sqrt((centerX_ + radius_ * std::cos(phi)) * (centerX_ + radius_ * std::cos(phi)) +
211  (centerY_ + radius_ * std::sin(phi)) * (centerY_ + radius_ * std::sin(phi)));
212 }
Sin< T >::type sin(const T &t)
Definition: Sin.h:22
#define X(str)
Definition: MuonsGrabber.cc:38
Implementation of a barrel detector layer (cylindrical).
static double constexpr speedOfLight
Speed of light [cm / ns].
Definition: Constants.h:12
bool crosses(const BarrelSimplifiedGeometry &layer) const override
Check if an intersection of the trajectory with a barrel layer exists.
T sqrt(T t)
Definition: SSEVec.h:19
Mathematical representation of a straight trajectory.
Cos< T >::type cos(const T &t)
Definition: Cos.h:22
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
#define M_PI
Definition the generic trajectory of a particle (base class for helix/straight trajectories).
Definition: Trajectory.h:26
void move(double deltaTimeC) override
Move the particle along the helix trajectory for a given time.
double b
Definition: hdecay.h:118
HelixTrajectory(const Particle &particle, double magneticFieldZ)
Constructor.
double getRadParticle(double phi) const
Return distance of particle from center of the detector if it was at given angle phi of the helix...
double a
Definition: hdecay.h:119
double nextCrossingTimeC(const BarrelSimplifiedGeometry &layer, bool onLayer=false) const override
Return delta time (t*c) of the next intersection of trajectory and barrel layer.
double nextCrossingTimeC(const BarrelSimplifiedGeometry &layer, bool onLayer=false) const override
Return delta time (t*c) of the next intersection of trajectory and barrel layer.
Definition of a generic FastSim Particle which can be propagated through the detector (formerly Parti...
Definition: Particle.h:16
static uInt32 F(BLOWFISH_CTX *ctx, uInt32 x)
Definition: blowfish.cc:163