8 #include <framework/dataobjects/Helix.h>
10 #include <framework/gearbox/Const.h>
12 #include <boost/math/special_functions/sign.hpp>
13 #include <boost/math/special_functions/sinc.hpp>
14 #include <boost/math/tools/precision.hpp>
16 #include <Math/VectorUtil.h>
22 using namespace HelixParameterIndex;
33 Helix::Helix(
const ROOT::Math::XYZVector& position,
34 const ROOT::Math::XYZVector& momentum,
35 const short int charge,
38 setCartesian(position,
momentum, charge, bZ);
45 const double& tanLambda)
50 m_tanLambda(tanLambda)
54 double Helix::getPerigeeX()
const
56 return getD0() * getSinPhi0();
59 double Helix::getPerigeeY()
const
61 return -getD0() * getCosPhi0();
64 double Helix::getPerigeeZ()
const
69 ROOT::Math::XYZVector Helix::getPerigee()
const
71 return ROOT::Math::XYZVector(getPerigeeX(), getPerigeeY(), getPerigeeZ());
74 double Helix::getMomentumX(
const double bZ)
const
76 return getCosPhi0() * getTransverseMomentum(bZ);
79 double Helix::getMomentumY(
const double bZ)
const
81 return getSinPhi0() * getTransverseMomentum(bZ);
84 double Helix::getMomentumZ(
const double bZ)
const
86 return getTanLambda() * getTransverseMomentum(bZ);
89 ROOT::Math::XYZVector Helix:: getMomentum(
const double bZ)
const
91 return ROOT::Math::XYZVector(getMomentumX(bZ), getMomentumY(bZ), getMomentumZ(bZ));
94 ROOT::Math::XYZVector Helix::getDirection()
const
96 return ROOT::Math::XYZVector(getCosPhi0(), getSinPhi0(), getTanLambda());
99 double Helix::getTransverseMomentum(
const double bZ)
const
101 return 1 / std::fabs(getAlpha(bZ) * getOmega());
104 double Helix::getKappa(
const double bZ)
const
106 return getOmega() * getAlpha(bZ);
109 double Helix::getAlpha(
const double bZ)
114 short Helix::getChargeSign()
const
116 return boost::math::sign(getOmega());
119 void Helix::reverse()
123 m_phi0 = reversePhi(m_phi0);
125 m_tanLambda = -m_tanLambda;
128 double Helix::getArcLength2DAtCylindricalR(
const double& cylindricalR)
const
135 const double dr = getD0();
136 const double deltaCylindricalR = cylindricalR;
137 const double absArcLength2D = calcArcLength2DAtDeltaCylindricalRAndDr(deltaCylindricalR,
dr);
138 return absArcLength2D;
141 double Helix::getArcLength2DAtXY(
const double& x,
const double& y)
const
144 double arcLength2D = 0;
145 calcArcLength2DAndDrAtXY(
x, y, arcLength2D,
dr);
149 double Helix::getArcLength2DAtNormalPlane(
const double& byX,
const double& byY,
150 const double& nX,
const double& nY)
const
153 const double tX = nY;
154 const double tY = -nX;
157 const double omega = getOmega();
158 const double cosPhi0 = getCosPhi0();
159 const double sinPhi0 = getSinPhi0();
160 const double d0 = getD0();
165 const double deltaOrthogonal = byY *
cosPhi0 - byX *
sinPhi0 + d0;
166 const double deltaCylindricalR = hypot(deltaOrthogonal, deltaParallel);
167 const double deltaCylindricalRSquared = deltaCylindricalR * deltaCylindricalR;
169 const double UOrthogonal = 1 + omega * deltaOrthogonal;
170 const double UParallel = omega * deltaParallel;
173 const double A = 2 * deltaOrthogonal + omega * deltaCylindricalRSquared;
177 const double tCylindricalR = (tX * tX + tY * tY);
179 const double c = A / 2;
180 const double b = UParallel * tParallel + UOrthogonal * tOrthogonal;
181 const double a = omega / 2 * tCylindricalR;
183 const double discriminant = ((double)b) * b - 4 *
a * c;
184 const double root =
sqrt(discriminant);
185 const double bigSum = (b > 0) ? -b - root : -b + root;
187 const double bigOffset = bigSum / 2 /
a;
188 const double smallOffset = 2 * c / bigSum;
190 const double distance1 = hypot(byX + bigOffset * tX, byY + bigOffset * tY);
191 const double distance2 = hypot(byX + smallOffset * tX, byY + smallOffset * tY);
193 if (distance1 < distance2) {
194 return getArcLength2DAtXY(byX + bigOffset * tX, byY + bigOffset * tY);
196 return getArcLength2DAtXY(byX + smallOffset * tX, byY + smallOffset * tY);
201 ROOT::Math::XYZVector Helix::getPositionAtArcLength2D(
const double& arcLength2D)
const
225 const double chi = -arcLength2D * getOmega();
226 const double chiHalf = chi / 2.0;
228 using boost::math::sinc_pi;
230 const double x = arcLength2D * sinc_pi(chi);
231 const double y = arcLength2D * sinc_pi(chiHalf) * sin(chiHalf) - getD0();
234 const double z = fma((
double)arcLength2D, getTanLambda(), getZ0());
237 ROOT::Math::XYZVector position(
x, y, z);
240 position = ROOT::Math::VectorUtil::RotateZ(position, getPhi0());
245 ROOT::Math::XYZVector Helix::getTangentialAtArcLength2D(
const double& arcLength2D)
const
247 const double omega = getOmega();
248 const double phi0 = getPhi0();
249 const double tanLambda = getTanLambda();
251 const double chi = - omega * arcLength2D;
253 const double tx = cos(chi +
phi0);
254 const double ty = sin(chi +
phi0);
255 const double tz = tanLambda;
257 ROOT::Math::XYZVector tangential(tx, ty, tz);
261 ROOT::Math::XYZVector Helix::getUnitTangentialAtArcLength2D(
const double& arcLength2D)
const
263 ROOT::Math::XYZVector unitTangential = getTangentialAtArcLength2D(arcLength2D);
264 const double norm = hypot(1, getTanLambda());
265 const double invNorm = 1 / norm;
266 unitTangential *= invNorm;
267 return unitTangential;
270 ROOT::Math::XYZVector Helix::getMomentumAtArcLength2D(
const double& arcLength2D,
const double& bz)
const
272 ROOT::Math::XYZVector
momentum = getTangentialAtArcLength2D(arcLength2D);
273 const double pr = getTransverseMomentum(bz);
279 double Helix::passiveMoveBy(
const double& byX,
285 double arcLength2D = 0;
286 calcArcLength2DAndDrAtXY(byX, byY, arcLength2D, new_d0);
289 double chi = -arcLength2D * getOmega();
290 double new_phi0 = m_phi0 + chi;
291 double new_z0 = getZ0() - byZ + getTanLambda() * arcLength2D;
301 TMatrixD Helix::calcPassiveMoveByJacobian(
const ROOT::Math::XYZVector& by,
const double expandBelowChi)
const
303 TMatrixD jacobian(5, 5);
304 calcPassiveMoveByJacobian(by.X(), by.Y(), jacobian, expandBelowChi);
309 void Helix::calcPassiveMoveByJacobian(
const double& byX,
312 const double expandBelowChi)
const
316 jacobian.UnitMatrix();
317 assert(jacobian.GetNrows() == jacobian.GetNcols());
318 assert(jacobian.GetNrows() == 5 or jacobian.GetNrows() == 6);
321 const double omega = getOmega();
322 const double cosPhi0 = getCosPhi0();
323 const double sinPhi0 = getSinPhi0();
324 const double d0 = getD0();
325 const double tanLambda = getTanLambda();
330 const double deltaOrthogonal = byY *
cosPhi0 - byX *
sinPhi0 + d0;
331 const double deltaCylindricalR = hypot(deltaOrthogonal, deltaParallel);
332 const double deltaCylindricalRSquared = deltaCylindricalR * deltaCylindricalR;
335 const double A = 2 * deltaOrthogonal + omega * deltaCylindricalRSquared;
336 const double USquared = 1 + omega * A;
337 const double U =
sqrt(USquared);
338 const double UOrthogonal = 1 + omega * deltaOrthogonal;
339 const double UParallel = omega * deltaParallel;
341 const double u = 1 + omega * d0;
352 const double new_d0 = A / (1 + U);
354 jacobian(iD0, iOmega) = (deltaCylindricalR + new_d0) * (deltaCylindricalR - new_d0) / 2 / U;
355 jacobian(iD0, iPhi0) = - u * deltaParallel / U;
356 jacobian(iD0, iD0) = UOrthogonal / U;
361 const double dArcLength2D_dD0 = - UParallel / USquared;
362 const double dArcLength2D_dPhi0 = (omega * deltaCylindricalRSquared + deltaOrthogonal - d0 * UOrthogonal) / USquared;
364 jacobian(iPhi0, iD0) = - dArcLength2D_dD0 * omega;
365 jacobian(iPhi0, iPhi0) = u * UOrthogonal / USquared;
366 jacobian(iPhi0, iOmega) = -deltaParallel / USquared;
375 const double chi = -atan2(UParallel, UOrthogonal);
376 double arcLength2D = 0;
377 double dArcLength2D_dOmega = 0;
379 if (fabs(chi) < std::min(expandBelowChi, M_PI / 2.0)) {
382 double principleArcLength2D = deltaParallel / UOrthogonal;
383 const double dPrincipleArcLength2D_dOmega = - principleArcLength2D * deltaOrthogonal / UOrthogonal;
385 const double x = principleArcLength2D * omega;
386 const double f = calcATanXDividedByX(
x);
387 const double df_dx = calcDerivativeOfATanXDividedByX(
x);
389 arcLength2D = principleArcLength2D * f;
390 dArcLength2D_dOmega = (f +
x * df_dx) * dPrincipleArcLength2D_dOmega + principleArcLength2D * df_dx * principleArcLength2D;
396 arcLength2D = - chi / omega;
397 dArcLength2D_dOmega = (-arcLength2D - jacobian(iPhi0, iOmega)) / omega;
404 jacobian(iZ0, iD0) = tanLambda * dArcLength2D_dD0;
405 jacobian(iZ0, iPhi0) = tanLambda * dArcLength2D_dPhi0;
406 jacobian(iZ0, iOmega) = tanLambda * dArcLength2D_dOmega;
407 jacobian(iZ0, iZ0) = 1.0;
408 jacobian(iZ0, iTanLambda) = arcLength2D;
410 if (jacobian.GetNrows() == 6) {
412 jacobian(iArcLength2D, iD0) = dArcLength2D_dD0;
413 jacobian(iArcLength2D, iPhi0) = dArcLength2D_dPhi0;
414 jacobian(iArcLength2D, iOmega) = dArcLength2D_dOmega;
415 jacobian(iArcLength2D, iZ0) = 0.0;
416 jacobian(iArcLength2D, iTanLambda) = 0;
417 jacobian(iArcLength2D, iArcLength2D) = 1.0;
421 double Helix::reversePhi(
const double& phi)
423 return std::remainder(phi + M_PI, 2 * M_PI);
426 double Helix::calcArcLength2DFromSecantLength(
const double& secantLength)
const
428 return secantLength * calcSecantLengthToArcLength2DFactor(secantLength);
432 double Helix::calcSecantLengthToArcLength2DFactor(
const double& secantLength)
const
434 double x = secantLength * getOmega() / 2.0;
435 return calcASinXDividedByX(
x);
438 double Helix::calcASinXDividedByX(
const double& x)
443 BOOST_MATH_STD_USING;
445 auto const taylor_n_bound = boost::math::tools::forth_root_epsilon<double>();
447 if (abs(
x) >= taylor_n_bound) {
460 auto const taylor_0_bound = boost::math::tools::epsilon<double>();
461 if (abs(
x) >= taylor_0_bound) {
466 auto const taylor_2_bound = boost::math::tools::root_epsilon<double>();
467 if (abs(
x) >= taylor_2_bound) {
469 result += x2 * x2 * (3.0 / 40.0);
477 double Helix::calcATanXDividedByX(
const double& x)
482 BOOST_MATH_STD_USING;
484 auto const taylor_n_bound = boost::math::tools::forth_root_epsilon<double>();
486 if (abs(
x) >= taylor_n_bound) {
493 auto const taylor_0_bound = boost::math::tools::epsilon<double>();
494 if (abs(
x) >= taylor_0_bound) {
499 auto const taylor_2_bound = boost::math::tools::root_epsilon<double>();
500 if (abs(
x) >= taylor_2_bound) {
502 result += x2 * x2 * (1.0 / 5.0);
509 double Helix::calcDerivativeOfATanXDividedByX(
const double& x)
514 BOOST_MATH_STD_USING;
516 auto const taylor_n_bound = boost::math::tools::forth_root_epsilon<double>();
518 const double x2 =
x *
x;
519 if (abs(
x) >= taylor_n_bound) {
520 const double chi =
atan(
x);
521 return ((1 - chi /
x) /
x - chi) / (1 + x2);
527 auto const taylor_0_bound = boost::math::tools::epsilon<double>();
528 if (abs(
x) >= taylor_0_bound) {
530 result -= 2.0 *
x / 3.0;
532 auto const taylor_2_bound = boost::math::tools::root_epsilon<double>();
533 if (abs(
x) >= taylor_2_bound) {
535 result += x2 *
x * (4.0 / 5.0);
545 void Helix::calcArcLength2DAndDrAtXY(
const double& x,
const double& y,
double& arcLength2D,
double& dr)
const
548 const double omega = getOmega();
549 const double cosPhi0 = getCosPhi0();
550 const double sinPhi0 = getSinPhi0();
551 const double d0 = getD0();
555 const double deltaCylindricalR = hypot(deltaOrthogonal, deltaParallel);
556 const double deltaCylindricalRSquared = deltaCylindricalR * deltaCylindricalR;
558 const double A = 2 * deltaOrthogonal + omega * deltaCylindricalRSquared;
559 const double U =
sqrt(1 + omega * A);
560 const double UOrthogonal = 1 + omega * deltaOrthogonal;
561 const double UParallel = omega * deltaParallel;
568 const double chi = -atan2(UParallel, UOrthogonal);
570 if (fabs(chi) < M_PI / 8) {
572 double principleArcLength2D = deltaParallel / UOrthogonal;
573 arcLength2D = principleArcLength2D * calcATanXDividedByX(principleArcLength2D * omega);
577 arcLength2D = -chi / omega;
581 double Helix::calcArcLength2DAtDeltaCylindricalRAndDr(
const double& deltaCylindricalR,
const double& dr)
const
583 const double omega = getOmega();
584 double secantLength =
sqrt((deltaCylindricalR +
dr) * (deltaCylindricalR -
dr) / (1 +
dr * omega));
585 return calcArcLength2DFromSecantLength(secantLength);
588 void Helix::setCartesian(
const ROOT::Math::XYZVector& position,
589 const ROOT::Math::XYZVector& momentum,
590 const short int charge,
593 assert(abs(charge) <= 1);
594 const double alpha = getAlpha(bZ);
601 const double x = position.X();
602 const double y = position.Y();
603 const double z = position.Z();
609 const double ptinv = 1 / hypot(px, py);
610 const double omega = charge * ptinv / alpha;
611 const double tanLambda = ptinv * pz;
612 const double phi0 = atan2(py, px);
619 m_tanLambda = tanLambda;
622 passiveMoveBy(-
x, -y, 0);
635 <<
"d0=" << helix.getD0() <<
", "
636 <<
"phi0=" << helix.getPhi0() <<
", "
637 <<
"omega=" << helix.getOmega() <<
", "
638 <<
"z0=" << helix.getZ0() <<
", "
639 <<
"tanLambda=" << helix.getTanLambda() <<
")";
Helix(const HepPoint3D &pivot, const HepVector &a, const HepSymMatrix &Ea)
Constructor with pivot, helix parameter a, and its error matrix.
double phi0(void) const
Return helix parameter phi0.
const HepVector & a(void) const
Returns helix parameters.
double dr(void) const
Return helix parameter dr.
Hep3Vector momentum(double dPhi=0.) const
returns momentum vector after rotating angle dPhi in phi direction.
HepPoint3D x(double dPhi=0.) const
returns position after rotating angle dPhi in phi direction.
double cosPhi0(void) const
Return cos phi0.
double sinPhi0(void) const
Return sin phi0.
static const double speedOfLight
[cm/ns]
std::ostream & operator<<(std::ostream &output, const IntervalOfValidity &iov)
double sqrt(double a)
sqrt for double
double atan(double a)
atan for double
Abstract base class for different kinds of events.