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